diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..ab27960a6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,126 @@ +# 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. +# +# The easiest way to apply the formatting to your changes ONLY, +# is to use the git-clang-format script (usually installed with clang-format). +# +# - Fix up formatting before committing +# 1. Edit and stage your files. +# 2. Run `git clang-format`. +# 3. Verify + correct + (un)stage changes. +# 4. Commit. +# +# - Fix up formatting after committing +# 1. Commit your changes. +# 2. Run `git clang-format HEAD~` - Refer the commit *before* your changes here. +# 3. Verify + correct changes, `git difftool -d` can help here. +# 4. Stage + commit, potentially with `--amend` (means to fixup the last commit). +# +# To run clang-format on all sourcefiles, use the following line: +# $ git ls-files 'src/*.[ch]' 'src/*.cc' | xargs clang-format -i -style=file +# +# You can find more information on the different config parameters in this file here: +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignEscapedNewlines: Left +AlignOperands: false +AllowShortFunctionsOnASingleLine: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterEnum: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBraces: Custom +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: false +BreakStringLiterals: false +ColumnLimit: 109 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +ForEachMacros: + - BITMAP_FOREACH + - CMSG_FOREACH + - _DNS_ANSWER_FOREACH + - DNS_ANSWER_FOREACH + - _DNS_ANSWER_FOREACH_FLAGS + - DNS_ANSWER_FOREACH_FLAGS + - _DNS_ANSWER_FOREACH_FULL + - DNS_ANSWER_FOREACH_FULL + - _DNS_ANSWER_FOREACH_IFINDEX + - DNS_ANSWER_FOREACH_IFINDEX + - _DNS_QUESTION_FOREACH + - DNS_QUESTION_FOREACH + - FDSET_FOREACH + - FOREACH_BTRFS_IOCTL_SEARCH_HEADER + - FOREACH_DEVICE + - FOREACH_DEVICE_AND_SUBSYSTEM + - FOREACH_DEVICE_DEVLINK + - FOREACH_DEVICE_PROPERTY + - FOREACH_DEVICE_SYSATTR + - FOREACH_DEVICE_TAG + - FOREACH_DIRENT + - FOREACH_DIRENT_ALL + - FOREACH_INOTIFY_EVENT + - FOREACH_STRING + - FOREACH_SUBSYSTEM + - _FOREACH_WORD + - FOREACH_WORD + - FOREACH_WORD_SEPARATOR + - HASHMAP_FOREACH + - HASHMAP_FOREACH_IDX + - HASHMAP_FOREACH_KEY + - JOURNAL_FOREACH_DATA_RETVAL + - JSON_VARIANT_ARRAY_FOREACH + - JSON_VARIANT_OBJECT_FOREACH + - LIST_FOREACH + - LIST_FOREACH_AFTER + - LIST_FOREACH_BEFORE + - LIST_FOREACH_OTHERS + - LIST_FOREACH_SAFE + - MESSAGE_FOREACH_PART + - NULSTR_FOREACH + - NULSTR_FOREACH_PAIR + - OBJECT_PATH_FOREACH_PREFIX + - ORDERED_HASHMAP_FOREACH + - ORDERED_HASHMAP_FOREACH_KEY + - ORDERED_SET_FOREACH + - PATH_FOREACH_PREFIX + - PATH_FOREACH_PREFIX_MORE + - SD_HWDB_FOREACH_PROPERTY + - SD_JOURNAL_FOREACH + - SD_JOURNAL_FOREACH_BACKWARDS + - SD_JOURNAL_FOREACH_DATA + - SD_JOURNAL_FOREACH_FIELD + - SD_JOURNAL_FOREACH_UNIQUE + - SECCOMP_FOREACH_LOCAL_ARCH + - SET_FOREACH + - SET_FOREACH_MOVE + - STRV_FOREACH + - STRV_FOREACH_BACKWARDS + - STRV_FOREACH_PAIR +IndentPPDirectives: AfterHash +IndentWidth: 8 +IndentWrappedFunctionNames: true +MaxEmptyLinesToKeep: 2 +PenaltyBreakAssignment: 65 +PenaltyBreakBeforeFirstCallParameter: 16 +PenaltyBreakComment: 320 +PenaltyBreakFirstLessLess: 50 +PenaltyBreakString: 0 +PenaltyExcessCharacter: 10 +PenaltyReturnTypeOnItsOwnLine: 100 +SpaceAfterCStyleCast: true +SpacesInAngles: true +TabWidth: 8 +UseCRLF: false diff --git a/.editorconfig b/.editorconfig index 63b1d749c..d24acc1f0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -26,3 +26,7 @@ indent_size = 4 [meson.build] indent_style = space indent_size = 8 + +[man/*.xml] +indent_size = 2 +indent_style = space diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 63ccea9a6..1ca822896 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -7,7 +7,8 @@ about: A report of an error in a recent systemd version **systemd version the issue has been seen with** > ... - + + **Used distribution** diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml new file mode 100644 index 000000000..3b86689d2 --- /dev/null +++ b/.github/workflows/build_test.yml @@ -0,0 +1,28 @@ +--- +# vi: ts=2 sw=2 et: +# +name: Build test +on: + pull_request: + paths: + - '**/meson.build' + - '.github/workflows/**' + - 'meson_options.txt' + - 'src/**' + - 'test/fuzz/**' + +jobs: + build: + runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + env: + - { COMPILER: "gcc", COMPILER_VERSION: "10" } + - { COMPILER: "clang", COMPILER_VERSION: "10" } + env: ${{ matrix.env }} + steps: + - name: Repository checkout + uses: actions/checkout@v1 + - name: Build check (${{ env.COMPILER }}-${{ env.COMPILER_VERSION }}) + run: sudo -E .github/workflows/ubuntu-build-check.sh diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 000000000..ed6db50d9 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,47 @@ +--- +# vi: ts=2 sw=2 et: +# See: https://google.github.io/oss-fuzz/getting-started/continuous-integration/ + +name: CIFuzz +on: + pull_request: + paths: + - '**/meson.build' + - '.github/workflows/**' + - 'meson_options.txt' + - 'src/**' + - 'test/fuzz/**' + - 'tools/oss-fuzz.sh' + push: + branches: + - master +jobs: + Fuzzing: + runs-on: ubuntu-latest + if: github.repository == 'systemd/systemd' + strategy: + fail-fast: false + matrix: + sanitizer: [address, undefined, memory] + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'systemd' + dry-run: false + allowed-broken-targets-percentage: 0 + sanitizer: ${{ matrix.sanitizer }} + - name: Run Fuzzers (${{ matrix.sanitizer }}) + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'systemd' + fuzz-seconds: 600 + dry-run: false + sanitizer: ${{ matrix.sanitizer }} + - name: Upload Crash + uses: actions/upload-artifact@v1 + if: failure() && steps.build.outcome == 'success' + with: + name: ${{ matrix.sanitizer }}-artifacts + path: ./out/artifacts diff --git a/.github/workflows/ubuntu-build-check.sh b/.github/workflows/ubuntu-build-check.sh new file mode 100755 index 000000000..75fc36fff --- /dev/null +++ b/.github/workflows/ubuntu-build-check.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +set -ex + +info() { echo -e "\033[33;1m$1\033[0m"; } +fatal() { echo >&2 -e "\033[31;1m$1\033[0m"; exit 1; } +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=false" + "-Db_ndebug=true" +) +PACKAGES=( + cryptsetup-bin + expect + fdisk + gettext + iptables-dev + iputils-ping + isc-dhcp-client + itstool + kbd + libblkid-dev + libcap-dev + libcurl4-gnutls-dev + libfdisk-dev + libgpg-error-dev + liblz4-dev + liblzma-dev + libmicrohttpd-dev + libmount-dev + libp11-kit-dev + libpwquality-dev + libqrencode-dev + libssl-dev + libxkbcommon-dev + libzstd-dev + mount + net-tools + perl + python-lxml + python3-evdev + python3-lxml + python3-pip + python3-pyparsing + python3-setuptools + quota + strace + unifont + util-linux + zstd +) +COMPILER="${COMPILER:?}" +COMPILER_VERSION="${COMPILER_VERSION:?}" +RELEASE="$(lsb_release -cs)" + +bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list" + +# Note: As we use postfixed clang/gcc binaries, we need to override $AR +# as well, otherwise meson falls back to ar from binutils which +# doesn't work with LTO +if [[ "$COMPILER" == clang ]]; then + CC="clang-$COMPILER_VERSION" + CXX="clang++-$COMPILER_VERSION" + AR="llvm-ar-$COMPILER_VERSION" + # Latest LLVM stack deb packages provided by https://apt.llvm.org/ + # Following snippet was borrowed from https://apt.llvm.org/llvm.sh + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - + add-apt-repository -y "deb http://apt.llvm.org/$RELEASE/ llvm-toolchain-$RELEASE-$COMPILER_VERSION main" + PACKAGES+=(clang-$COMPILER_VERSION lldb-$COMPILER_VERSION lld-$COMPILER_VERSION clangd-$COMPILER_VERSION) +elif [[ "$COMPILER" == gcc ]]; then + CC="gcc-$COMPILER_VERSION" + CXX="g++-$COMPILER_VERSION" + AR="gcc-ar-$COMPILER_VERSION" + # Latest gcc stack deb packages provided by + # https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + PACKAGES+=(gcc-$COMPILER_VERSION) +else + fatal "Unknown compiler: $COMPILER" +fi + +# PPA with some newer build dependencies (like zstd) +add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci +apt-get -y update +apt-get -y build-dep systemd +apt-get -y install "${PACKAGES[@]}" +# Install the latest meson and ninja form pip, since the distro versions don't +# support all the features we need (like --optimization=). Since the build-dep +# command above installs the distro versions, let's install the pip ones just +# locally and add the local bin directory to the $PATH. +pip3 install --user -U meson ninja +export PATH="$HOME/.local/bin:$PATH" + +$CC --version + +for args in "${ARGS[@]}"; do + SECONDS=0 + + info "Checking build with $args" + if ! AR="$AR" CC="$CC" CXX="$CXX" CFLAGS="-Werror" CXXFLAGS="-Werror" meson -Dtests=unsafe -Dslow-tests=true --werror $args build; then + fatal "meson failed with $args" + fi + + ninja --version + if ! ninja -C build; then + fatal "ninja failed with $args" + fi + + git clean -dxf + + success "Build with $args passed in $SECONDS seconds" +done diff --git a/.gitignore b/.gitignore index 5d1870553..0b2092d74 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ .config.args .gdb_history .deps/ +.mypy_cache/ +__pycache__/ /*.gcda /*.gcno /*.tar.bz2 @@ -33,5 +35,5 @@ /.mkosi-* /mkosi.builddir/ /mkosi.output/ +/mkosi.default /tags -__pycache__/ diff --git a/.mailmap b/.mailmap index 3f3af64d7..662ea13a6 100644 --- a/.mailmap +++ b/.mailmap @@ -118,6 +118,7 @@ Michael Olbrich Michal Soltys Michal Suchanek Michal Suchanek +Michal Sekletár Michał Szczepański Michel Kraus <27o@users.noreply.github.com> Miklos Vajna @@ -206,3 +207,5 @@ Roger James Stephan Edel Andrey Yashkin <38919268+AndreyYashkin@users.noreply.github.com> Ronald Tschalär +Jay Burger +Yi Gao diff --git a/.mkosi/mkosi.arch b/.mkosi/mkosi.arch index 350d7cd2b..cb1952206 100644 --- a/.mkosi/mkosi.arch +++ b/.mkosi/mkosi.arch @@ -3,7 +3,7 @@ # Copyright © 2016 Zeal Jagannatha # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). -# Simply invoke "mkosi" in the project directory to build an OS image. +# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image. [Distribution] Distribution=arch @@ -52,6 +52,7 @@ BuildPackages= python-lxml qrencode xz + zstd Packages= libidn2 diff --git a/.mkosi/mkosi.debian b/.mkosi/mkosi.debian index e85612bef..db9bd3550 100644 --- a/.mkosi/mkosi.debian +++ b/.mkosi/mkosi.debian @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1+ # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). -# Simply invoke "mkosi" in the project directory to build an OS image. +# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image. [Distribution] Distribution=debian @@ -50,6 +50,7 @@ BuildPackages= libsmartcols-dev libtool libxkbcommon-dev + libzstd-dev m4 meson pkg-config @@ -59,6 +60,7 @@ BuildPackages= uuid-dev xsltproc xz-utils + zstd Packages= libqrencode4 diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora index 01bfd2338..09527711f 100644 --- a/.mkosi/mkosi.fedora +++ b/.mkosi/mkosi.fedora @@ -1,11 +1,11 @@ # SPDX-License-Identifier: LGPL-2.1+ # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). -# Simply invoke "mkosi" in the project directory to build an OS image. +# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image. [Distribution] Distribution=fedora -Release=31 +Release=32 [Output] Format=gpt_ext4 @@ -31,7 +31,6 @@ BuildPackages= gnu-efi-devel gnutls-devel gperf - hostname iptables-devel kmod-devel libacl-devel @@ -48,6 +47,7 @@ BuildPackages= libselinux-devel libxkbcommon-devel libxslt + libzstd-devel lz4 lz4-devel m4 @@ -63,16 +63,13 @@ BuildPackages= tree valgrind-devel xz-devel + zstd Packages= - coreutils - cryptsetup-libs - kmod-libs - e2fsprogs - libidn2 - libseccomp + # libzstd can be dropped once the Fedora RPM gets a dependency on it + libzstd + # procps-ng provides a set of useful utilies (ps, free, etc) procps-ng - util-linux BuildDirectory=mkosi.builddir Cache=mkosi.cache diff --git a/.mkosi/mkosi.opensuse b/.mkosi/mkosi.opensuse new file mode 100644 index 000000000..53837b6be --- /dev/null +++ b/.mkosi/mkosi.opensuse @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: LGPL-2.1+ + +# 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=opensuse +Release=tumbleweed + +[Output] +Format=raw_btrfs +Bootable=yes + +[Partitions] +RootSize=3G + +[Packages] +# Uncomment to share system RPM cache (works only with Tumbleweed) +#Cache=/var/cache/zypp/packages +BuildDirectory=mkosi.builddir +BuildPackages= + docbook-xsl-stylesheets + fdupes + gcc + gnu-efi + gperf + intltool + libacl-devel + libapparmor-devel + libblkid-devel + libbz2-devel + libcap-devel + libcryptsetup-devel + libcurl-devel + libgcrypt-devel + libkmod-devel + liblz4-devel + libmicrohttpd-devel + libmount-devel + libseccomp-devel + libselinux-devel + libxslt-tools + m4 + meson + pam-devel + pciutils-devel + pcre-devel + python3 + python3-lxml + qrencode-devel + system-user-nobody + systemd-sysvinit + zlib-devel +# to satisfy tests + acl + diffutils + glibc-locale + system-group-obsolete + system-user-bin + system-user-daemon + system-user-root + timezone + +Packages= + # brought in via meson->python3 + libp11-kit0 + # --bootable=no + dbus-1 + libapparmor1 + libcrypt1 + libcryptsetup12 + libkmod2 + liblz4-1 + libmount1 + libqrencode4 + libseccomp2 + pam + util-linux diff --git a/.mkosi/mkosi.ubuntu b/.mkosi/mkosi.ubuntu index 1e4005f07..8dcc67a66 100644 --- a/.mkosi/mkosi.ubuntu +++ b/.mkosi/mkosi.ubuntu @@ -1,11 +1,11 @@ # SPDX-License-Identifier: LGPL-2.1+ # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). -# Simply invoke "mkosi" in the project directory to build an OS image. +# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image. [Distribution] Distribution=ubuntu -Release=bionic +Release=focal Repositories=main,universe [Output] @@ -25,7 +25,6 @@ BuildPackages= git gnu-efi gperf - iptables-dev libacl1-dev libaudit-dev libblkid-dev @@ -39,6 +38,8 @@ BuildPackages= libgcrypt20-dev libgnutls28-dev libidn2-0-dev + libip4tc-dev + libip6tc-dev libkmod-dev liblz4-dev liblz4-tool @@ -51,6 +52,8 @@ BuildPackages= libsmartcols-dev libtool libxkbcommon-dev + libxtables-dev + libzstd-dev m4 meson pkg-config @@ -61,8 +64,9 @@ BuildPackages= uuid-dev xsltproc xz-utils + zstd Packages= - libqrencode3 + libqrencode4 locales libidn2-0 diff --git a/.travis.yml b/.travis.yml index 82f62a860..50f8e6a23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,158 +1,84 @@ -sudo: required -dist: xenial +--- +# vi: ts=2 sw=2 et: + +language: bash +dist: bionic services: - - docker + - docker env: - global: - - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")" - - CI_MANAGERS="$TRAVIS_BUILD_DIR/travis-ci/managers" - - CI_TOOLS="$TRAVIS_BUILD_DIR/travis-ci/tools" - - REPO_ROOT="$TRAVIS_BUILD_DIR" + global: + - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")" + - CI_MANAGERS="$TRAVIS_BUILD_DIR/travis-ci/managers" + - CI_TOOLS="$TRAVIS_BUILD_DIR/travis-ci/tools" + - REPO_ROOT="$TRAVIS_BUILD_DIR" + jobs: + - DEBIAN_RELEASE=testing PHASE="RUN_GCC" + - DEBIAN_RELEASE=testing PHASE="RUN_GCC_ASAN_UBSAN" + - DEBIAN_RELEASE=testing PHASE="RUN_CLANG" + - DEBIAN_RELEASE=testing PHASE="RUN_CLANG_ASAN_UBSAN" stages: - - name: Build & test - if: type != cron - - - name: Fuzzit-Fuzzing - if: type = cron - - - name: Fuzzit-Regression - if: type != cron + # 'Test' is the default stage (for matrix jobs) + - name: Test + if: type != cron # Run Coverity periodically instead of for each commit/PR - - name: Coverity - if: type = cron + - name: Coverity + if: type = cron +# Matrix job definition - this is run for each combination of env variables +# from the env.jobs array above +before_install: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version +install: + - $CI_MANAGERS/debian.sh SETUP +script: + - $CI_MANAGERS/debian.sh $PHASE || travis_terminate 1 +after_script: + - $CI_MANAGERS/debian.sh CLEANUP + +# Inject another (single) job into the matrix for Coverity jobs: - include: - - stage: Build & test - name: Debian Testing - language: bash - env: - - DEBIAN_RELEASE="testing" - - CONT_NAME="systemd-debian-$DEBIAN_RELEASE" - - DOCKER_EXEC="docker exec -ti $CONT_NAME" - before_install: - - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - - docker --version - install: - - $CI_MANAGERS/debian.sh SETUP - script: - - set -e - # Build systemd - - $CI_MANAGERS/debian.sh RUN - - set +e - after_script: - - $CI_MANAGERS/debian.sh CLEANUP + include: + - stage: Coverity + language: bash + env: + - FEDORA_RELEASE="31" + - TOOL_BASE="/var/tmp/coverity-scan-analysis" + - CONT_NAME="coverity-fedora-$FEDORA_RELEASE" + - DOCKER_EXEC="docker exec -ti $CONT_NAME" + - DOCKER_RUN="docker run -v $TOOL_BASE:$TOOL_BASE:rw --env-file .cov-env" + # Coverity env variables + - PLATFORM="$(uname)" + - TOOL_ARCHIVE="/var/tmp/cov-analysis-$PLATFORM.tgz" + - SCAN_URL="https://scan.coverity.com" + - UPLOAD_URL="https://scan.coverity.com/builds" + - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG" + - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}" + - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH" + # Encrypted COVERITY_SCAN_TOKEN env variable + # Generated using `travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=xxxx` + - secure: "jKSz+Y1Mv8xMpQHh7g5lzW7E6HQGndFz/vKDJQ1CVShwFoyjV3Zu+MFS3UYKlh1236zL0Z4dvsYFx/b3Hq8nxZWCrWeZs2NdXgy/wh8LZhxwzcGYigp3sIA/cYdP5rDjFJO0MasNkl25/rml8+eZWz+8/xQic98UQHjSco/EOWtssoRcg0J0c4eDM7bGLfIQWE73NNY1Q1UtWjKmx1kekVrM8dPmHXJ9aERka7bmcbJAcKd6vabs6DQ5AfWccUPIn/EsRYqIJTRxJrFYU6XizANZ1a7Vwk/DWHZUEn2msxcZw5BbAMDTMx0TbfrNkKSHMHuvQUCu6KCBAq414i+LgkMfmQ2SWwKiIUsud1kxXX3ZPl9bxDv1HkvVdcniC/EM7lNEEVwm4meOnjuhI2lhOyOjmP3FTSlMHGP7xlK8DS2k9fqL58vn0BaSjwWgd+2+HuL2+nJmxcK1eLGzKqaostFxrk2Xs2vPZkUdV2nWY/asUrcWHml6YlWDn2eP83pfwxHYsMiEHY/rTKvxeVY+iirO/AphoO+eaYu7LvjKZU1Yx5Z4u/SnGWAiCH0yhMis0bWmgi7SCbw+sDd2uya+aoiLIGiB2ChW7hXHXCue/dif6/gLU7b+L8R00pQwnWdvKUPoIJCmZJYCluTeib4jpW+EmARB2+nR8wms2K9FGKM=" + before_install: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version + install: + # Install Coverity on the host + - $CI_TOOLS/get-coverity.sh + # Export necessary env variables for Coverity + - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env + # Pull a Docker image and start a new container + - $CI_MANAGERS/fedora.sh SETUP + script: + - set -e + # Preconfigure with meson to prevent Coverity from capturing meson metadata + - $DOCKER_EXEC meson cov-build -Dman=false + # Run Coverity + - $DOCKER_EXEC tools/coverity.sh build + - $DOCKER_EXEC tools/coverity.sh upload - - name: Debian Testing (ASan+UBSan) - language: bash - env: - - DEBIAN_RELEASE="testing" - - CONT_NAME="systemd-debian-$DEBIAN_RELEASE" - - DOCKER_EXEC="docker exec -ti $CONT_NAME" - before_install: - - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - - docker --version - install: - - $CI_MANAGERS/debian.sh SETUP - script: - - set -e - - $CI_MANAGERS/debian.sh RUN_ASAN - - set +e - after_script: - - $CI_MANAGERS/debian.sh CLEANUP - - - name: Debian Testing (clang) - language: bash - env: - - DEBIAN_RELEASE="testing" - - CONT_NAME="systemd-debian-$DEBIAN_RELEASE" - - DOCKER_EXEC="docker exec -ti $CONT_NAME" - before_install: - - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - - docker --version - install: - - $CI_MANAGERS/debian.sh SETUP - script: - - set -e - - $CI_MANAGERS/debian.sh RUN_CLANG - - set +e - after_script: - - $CI_MANAGERS/debian.sh CLEANUP - - - name: Debian Testing (clang ASan+UBSan) - language: bash - env: - - DEBIAN_RELEASE="testing" - - CONT_NAME="systemd-debian-$DEBIAN_RELEASE" - - DOCKER_EXEC="docker exec -ti $CONT_NAME" - before_install: - - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - - docker --version - install: - - $CI_MANAGERS/debian.sh SETUP - script: - - set -e - - $CI_MANAGERS/debian.sh RUN_CLANG_ASAN - - set +e - after_script: - - $CI_MANAGERS/debian.sh CLEANUP - - - stage: Fuzzit-Regression - name: Continuous Fuzzing via Fuzzit (regression) - language: bash - script: - - set -e - - $CI_MANAGERS/fuzzit.sh regression - - set +e - - - stage: Fuzzit-Fuzzing - name: Continuous Fuzzing via Fuzzit (fuzzing daily) - language: bash - script: - - set -e - - $CI_MANAGERS/fuzzit.sh fuzzing - - set +e - - - stage: Coverity - language: bash - env: - - FEDORA_RELEASE="latest" - - CONT_NAME="coverity-fedora-$FEDORA_RELEASE" - - DOCKER_EXEC="docker exec -ti $CONT_NAME" - - TOOL_BASE="/var/tmp/coverity-scan-analysis" - - DOCKER_RUN="docker run -v $TOOL_BASE:$TOOL_BASE:rw --env-file .cov-env" - # Coverity env variables - - PLATFORM="$(uname)" - - TOOL_ARCHIVE="/var/tmp/cov-analysis-$PLATFORM.tgz" - - SCAN_URL="https://scan.coverity.com" - - UPLOAD_URL="https://scan.coverity.com/builds" - - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG" - - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}" - - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH" - # Encrypted COVERITY_SCAN_TOKEN env variable - # Generated using `travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=xxxx` - - secure: "jKSz+Y1Mv8xMpQHh7g5lzW7E6HQGndFz/vKDJQ1CVShwFoyjV3Zu+MFS3UYKlh1236zL0Z4dvsYFx/b3Hq8nxZWCrWeZs2NdXgy/wh8LZhxwzcGYigp3sIA/cYdP5rDjFJO0MasNkl25/rml8+eZWz+8/xQic98UQHjSco/EOWtssoRcg0J0c4eDM7bGLfIQWE73NNY1Q1UtWjKmx1kekVrM8dPmHXJ9aERka7bmcbJAcKd6vabs6DQ5AfWccUPIn/EsRYqIJTRxJrFYU6XizANZ1a7Vwk/DWHZUEn2msxcZw5BbAMDTMx0TbfrNkKSHMHuvQUCu6KCBAq414i+LgkMfmQ2SWwKiIUsud1kxXX3ZPl9bxDv1HkvVdcniC/EM7lNEEVwm4meOnjuhI2lhOyOjmP3FTSlMHGP7xlK8DS2k9fqL58vn0BaSjwWgd+2+HuL2+nJmxcK1eLGzKqaostFxrk2Xs2vPZkUdV2nWY/asUrcWHml6YlWDn2eP83pfwxHYsMiEHY/rTKvxeVY+iirO/AphoO+eaYu7LvjKZU1Yx5Z4u/SnGWAiCH0yhMis0bWmgi7SCbw+sDd2uya+aoiLIGiB2ChW7hXHXCue/dif6/gLU7b+L8R00pQwnWdvKUPoIJCmZJYCluTeib4jpW+EmARB2+nR8wms2K9FGKM=" - before_install: - - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - - docker --version - install: - # Install Coverity on the host - - $CI_TOOLS/get-coverity.sh - # Export necessary env variables for Coverity - - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env - # Pull a Docker image and start a new container - - $CI_MANAGERS/fedora.sh SETUP - script: - - set -e - # Preconfigure with meson to prevent Coverity from capturing meson metadata - - $DOCKER_EXEC meson cov-build -Dman=false - # Run Coverity - - $DOCKER_EXEC tools/coverity.sh build - - $DOCKER_EXEC tools/coverity.sh upload - - - set +e - after_script: - - $CI_MANAGERS/fedora.sh CLEANUP + - set +e + after_script: + - $CI_MANAGERS/fedora.sh CLEANUP diff --git a/NEWS b/NEWS index c24c865be..95685ed7f 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,609 @@ systemd System and Service Manager +CHANGES WITH 246: + + * The service manager gained basic support for cgroup v2 freezer. Units + can now be suspended or resumed either using new systemctl verbs, + freeze and thaw respectively, or via D-Bus. + + * PID 1 may now automatically load pre-compiled AppArmor policies from + /etc/apparmor/earlypolicy during early boot. + + * The CPUAffinity= setting in service unit files now supports a new + special value "numa" that causes the CPU affinity masked to be set + based on the NUMA mask. + + * systemd will now log about all left-over processes remaining in a + unit when the unit is stopped. It will now warn about services using + KillMode=none, as this is generally an unsafe thing to make use of. + + * Two new unit file settings + ConditionPathIsEncrypted=/AssertPathIsEncrypted= have been + added. They may be used to check whether a specific file system path + resides on a block device that is encrypted on the block level + (i.e. using dm-crypt/LUKS). + + * Another pair of new settings ConditionEnvironment=/AssertEnvironment= + has been added that may be used for simple environment checks. This + is particularly useful when passing in environment variables from a + container manager (or from PAM in case of the systemd --user + instance). + + * .service unit files now accept a new setting CoredumpFilter= which + allows configuration of the memory sections coredumps of the + service's processes shall include. + + * .mount units gained a new ReadWriteOnly= boolean option. If set + it will not be attempted to mount a file system read-only if mounting + in read-write mode doesn't succeed. An option x-systemd.rw-only is + available in /etc/fstab to control the same. + + * .socket units gained a new boolean setting PassPacketInfo=. If + enabled, the kernel will attach additional per-packet metadata to all + packets read from the socket, as an ancillary message. This controls + the IP_PKTINFO, IPV6_RECVPKTINFO, NETLINK_PKTINFO socket options, + depending on socket type. + + * .service units gained a new setting RootHash= which may be used to + specify the root hash for verity enabled disk images which are + specified in RootImage=. RootVerity= may be used to specify a path to + the Verity data matching a RootImage= file system. (The latter is + only useful for images that do not contain the Verity data embedded + into the same image that carries a GPT partition table following the + Discoverable Partition Specification). Similarly, systemd-nspawn + gained a new switch --verity-data= that takes a path to a file with + the verity data of the disk image supplied in --image=, if the image + doesn't contain the verity data itself. + + * .service units gained a new setting RootHashSignature= which takes + either a base64 encoded PKCS#7 signature of the root hash specified + with RootHash=, or a path to a file to read the signature from. This + allows validation of the root hash against public keys available in + the kernel keyring, and is only supported on recent kernels + (>= 5.4)/libcryptsetup (>= 2.30). A similar switch has been added to + systemd-nspawn and systemd-dissect (--root-hash-sig=). Support for + this mechanism has also been added to systemd-veritysetup. + + * .service unit files gained two new options + TimeoutStartFailureMode=/TimeoutStopFailureMode= that may be used to + tune behaviour if a start or stop timeout is hit, i.e. whether to + terminate the service with SIGTERM, SIGABRT or SIGKILL. + + * Most options in systemd that accept hexadecimal values prefixed with + 0x in additional to the usual decimal notation now also support octal + notation when the 0o prefix is used and binary notation if the 0b + prefix is used. + + * Various command line parameters and configuration file settings that + configure key or certificate files now optionally take paths to + AF_UNIX sockets in the file system. If configured that way a stream + connection is made to the socket and the required data read from + it. This is a simple and natural extension to the existing regular + file logic, and permits other software to provide keys or + certificates via simple IPC services, for example when unencrypted + storage on disk is not desired. Specifically, systemd-networkd's + Wireguard and MACSEC key file settings as well as + systemd-journal-gatewayd's and systemd-journal-remote's PEM + key/certificate parameters support this now. + + * Unit files, tmpfiles.d/ snippets, sysusers.d/ snippets and other + configuration files that support specifier expansion learnt six new + specifiers: %a resolves to the current architecture, %o/%w/%B/%W + resolve to the various ID fields from /etc/os-release, %l resolves to + the "short" hostname of the system, i.e. the hostname configured in + the kernel truncated at the first dot. + + * Support for the .include syntax in unit files has been removed. The + concept has been obsolete for 6 years and we started warning about + its pending removal 2 years ago (also see NEWS file below). It's + finally gone now. + + * StandardError= and StandardOutput= in unit files no longer support + the "syslog" and "syslog-console" switches. They were long removed + from the documentation, but will now result in warnings when used, + and be converted to "journal" and "journal+console" automatically. + + * If the service setting User= is set to the "nobody" user, a warning + message is now written to the logs (but the value is nonetheless + accepted). Setting User=nobody is unsafe, since the primary purpose + of the "nobody" user is to own all files whose owner cannot be mapped + locally. It's in particular used by the NFS subsystem and in user + namespacing. By running a service under this user's UID it might get + read and even write access to all these otherwise unmappable files, + which is quite likely a major security problem. + + * tmpfs mounts automatically created by systemd (/tmp, /run, /dev/shm, + and others) now have a size and inode limits applied (50% of RAM for + /tmp and /dev/shm, 10% of RAM for other mounts, etc.) + + * nss-mymachines lost support for resolution of users and groups, and + now only does resolution of hostnames. This functionality is now + provided by nss-systemd. Thus, the 'mymachines' entry should be + removed from the 'passwd:' and 'group:' lines in /etc/nsswitch.conf + (and 'systemd' added if it is not already there). + + * A new kernel command line option systemd.hostname= has been added + that allows controlling the hostname that is initialized early during + boot. + + * A kernel command line option "udev.blockdev_read_only" has been + added. If specified all hardware block devices that show up are + immediately marked as read-only by udev. This option is useful for + making sure that a specific boot under no circumstances modifies data + on disk. Use "blockdev --setrw" to undo the effect of this, per + device. + + * A new boolean kernel command line option systemd.swap= has been + added, which may be used to turn off automatic activation of swap + devices listed in /etc/fstab. + + * New kernel command line options systemd.condition-needs-update= and + systemd.condition-first-boot= have been added, which override the + result of the ConditionNeedsUpdate= and ConditionFirstBoot= + conditions. + + * A new kernel command line option systemd.clock-usec= has been added + that allows setting the system clock to the specified time in µs + since Jan 1st, 1970 early during boot. This is in particular useful + in order to make test cases more reliable. + + * The fs.suid_dumpable sysctl is set to 2 / "suidsafe". This allows + systemd-coredump to save core files for suid processes. When saving + the core file, systemd-coredump will use the effective uid and gid of + the process that faulted. + + * The /sys/module/kernel/parameters/crash_kexec_post_notifiers file is + now automatically set to "Y" at boot, in order to enable pstore + generation for collection with systemd-pstore. + + * A new 'hwdb' file has been added that collects information about PCI + and USB devices that correctly support auto-suspend, on top of the + databases for this we import from the ChromiumOS project. If you have + a device that supports auto-suspend correctly and where it should be + enabled by default, please submit a patch that adds it to the + database (see /usr/lib/udev/hwdb.d/60-autosuspend.hwdb). + + * systemd-udevd gained the new configuration option timeout_signal= as well + as a corresponding kernel command line option udev.timeout_signal=. + The option can be used to configure the UNIX signal that the main + daemon sends to the worker processes on timeout. Setting the signal + to SIGABRT is useful for debugging. + + * .link files managed by systemd-udevd gained options RxFlowControl=, + TxFlowControl=, AutoNegotiationFlowControl= in the [Link] section, in + order to configure various flow control parameters. They also gained + RxMiniBufferSize= and RxJumboBufferSize= in order to configure jumbo + frame ring buffer sizes. + + * networkd.conf gained a new boolean setting ManageForeignRoutes=. If + enabled systemd-networkd manages all routes configured by other tools. + + * .network files managed by systemd-networkd gained a new section + [SR-IOV], in order to configure SR-IOV capable network devices. + + * systemd-networkd's [IPv6Prefix] section in .network files gained a + new boolean setting Assign=. If enabled an address from the prefix is + automatically assigned to the interface. + + * systemd-networkd gained a new section [DHCPv6PrefixDelegation] which + controls delegated prefixes assigned by DHCPv6 client. The section + has three settings: SubnetID=, Assign=, and Token=. The setting + SubnetID= allows explicit configuration of the preferred subnet that + systemd-networkd's Prefix Delegation logic assigns to interfaces. If + Assign= is enabled (which is the default) an address from any acquired + delegated prefix is automatically chosen and assigned to the + interface. The setting Token= specifies an optional address generation + mode for Assign=. + + * systemd-networkd's [Network] section gained a new setting + IPv4AcceptLocal=. If enabled the interface accepts packets with local + source addresses. + + * systemd-networkd gained support for configuring the HTB queuing + discipline in the [HierarchyTokenBucket] and + [HierarchyTokenBucketClass] sections. Similar the "pfifo" qdisc may + be configured in the [PFIFO] section, "GRED" in + [GenericRandomEarlyDetection], "SFB" in [StochasticFairBlue], "cake" + in [CAKE], "PIE" in [PIE], "DRR" in [DeficitRoundRobinScheduler] and + [DeficitRoundRobinSchedulerClass], "BFIFO" in [BFIFO], + "PFIFOHeadDrop" in [PFIFOHeadDrop], "PFIFOFast" in [PFIFOFast], "HHF" + in [HeavyHitterFilter], "ETS" in [EnhancedTransmissionSelection] and + "QFQ" in [QuickFairQueueing] and [QuickFairQueueingClass]. + + * systemd-networkd gained support for a new Termination= setting in the + [CAN] section for configuring the termination resistor. It also + gained a new ListenOnly= setting for controlling whether to only + listen on CAN interfaces, without interfering with traffic otherwise + (which is useful for debugging/monitoring CAN network + traffic). DataBitRate=, DataSamplePoint=, FDMode=, FDNonISO= have + been added to configure various CAN-FD aspects. + + * systemd-networkd's [DHCPv6] section gained a new option WithoutRA=. + When enabled, DHCPv6 will be attempted right-away without requiring an + Router Advertisement packet suggesting it first (i.e. without the 'M' + or 'O' flags set). The [IPv6AcceptRA] section gained a boolean option + DHCPv6Client= that may be used to turn off the DHCPv6 client even if + the RA packets suggest it. + + * systemd-networkd's [DHCPv4] section gained a new setting UseGateway= + which may be used to turn off use of the gateway information provided + by the DHCP lease. A new FallbackLeaseLifetimeSec= setting may be + used to configure how to process leases that lack a lifetime option. + + * systemd-networkd's [DHCPv4] and [DHCPServer] sections gained a new + setting SendVendorOption= allowing configuration of additional vendor + options to send in the DHCP requests/responses. The [DHCPv6] section + gained a new SendOption= setting for sending arbitrary DHCP + options. RequestOptions= has been added to request arbitrary options + from the server. UserClass= has been added to set the DHCP user class + field. + + * systemd-networkd's [DHCPServer] section gained a new set of options + EmitPOP3=/POP3=, EmitSMTP=/SMTP=, EmitLPR=/LPR= for including server + information about these three protocols in the DHCP lease. It also + gained support for including "MUD" URLs ("Manufacturer Usage + Description"). Support for "MUD" URLs was also added to the LLDP + stack, configurable in the [LLDP] section in .network files. + + * The Mode= settings in [MACVLAN] and [MACVTAP] now support 'source' + mode. Also, the sections now support a new setting SourceMACAddress=. + + * systemd-networkd's .netdev files now support a new setting + VLANProtocol= in the [Bridge] section that allows configuration of + the VLAN protocol to use. + + * systemd-networkd supports a new Group= setting in the [Link] section + of the .network files, to control the link group. + + * systemd-networkd's [Network] section gained a new + IPv6LinkLocalAddressGenerationMode= setting, which specifies how IPv6 + link local address is generated. + + * A new default .network file is now shipped that matches TUN/TAP + devices that begin with "vt-" in their name. Such interfaces will + have IP routing onto the host links set up automatically. This is + supposed to be used by VM managers to trivially acquire a network + interface which is fully set up for host communication, simply by + carefully picking an interface name to use. + + * systemd-networkd's [DHCPv6] section gained a new setting RouteMetric= + which sets the route priority for routes specified by the DHCP server. + + * systemd-networkd's [DHCPv6] section gained a new setting VendorClass= + which configures the vendor class information sent to DHCP server. + + * The BlackList= settings in .network files' [DHCPv4] and + [IPv6AcceptRA] sections have been renamed DenyList=. The old names + are still understood to provide compatibility. + + * networkctl gained the new "forcerenew" command for forcing all DHCP + server clients to renew their lease. The interface "status" output + will now show numerous additional fields of information about an + interface. There are new "up" and "down" commands to bring specific + interfaces up or down. + + * systemd-resolved's DNS= configuration option now optionally accepts a + port number (after ":") and a host name (after "#"). When the host + name is specified, the DNS-over-TLS certificate is validated to match + the specified hostname. Additionally, in case of IPv6 addresses, an + interface may be specified (after "%"). + + * systemd-resolved may be configured to forward single-label DNS names. + This is not standard-conformant, but may make sense in setups where + public DNS servers are not used. + + * systemd-resolved's DNS-over-TLS support gained SNI validation. + + * systemd-nspawn's --resolv-conf= switch gained a number of new + supported values. Specifically, options starting with "replace-" are + like those prefixed "copy-" but replace any existing resolv.conf + file. And options ending in "-uplink" and "-stub" can now be used to + propagate other flavours of resolv.conf into the container (as + defined by systemd-resolved). + + * The various programs included in systemd can now optionally output + their log messages on stderr prefixed with a timestamp, controlled by + the $SYSTEMD_LOG_TIME environment variable. + + * systemctl gained a new "-P" switch that is a shortcut for "--value + --property=…". + + * "systemctl list-units" and "systemctl list-machines" no longer hide + their first output column with --no-legend. To hide the first column, + use --plain. + + * "systemctl reboot" takes the option "--reboot-argument=". + The optional positional argument to "systemctl reboot" is now + being deprecated in favor of this option. + + * systemd-run gained a new switch --slice-inherit. If specified the + unit it generates is placed in the same slice as the systemd-run + process itself. + + * systemd-journald gained support for zstd compression of large fields + in journal files. The hash tables in journal files have been hardened + against hash collisions. This is an incompatible change and means + that journal files created with new systemd versions are not readable + with old versions. If the $SYSTEMD_JOURNAL_KEYED_HASH boolean + environment variable for systemd-journald.service is set to 0 this + new hardening functionality may be turned off, so that generated + journal files remain compatible with older journalctl + implementations. + + * journalctl will now include a clickable link in the default output for + each log message for which an URL with further documentation is + known. This is only supported on terminal emulators that support + clickable hyperlinks, and is turned off if a pager is used (since + "less" still doesn't support hyperlinks, + unfortunately). Documentation URLs may be included in log messages + either by including a DOCUMENTATION= journal field in it, or by + associating a journal message catalog entry with the log message's + MESSAGE_ID, which then carries a "Documentation:" tag. + + * journald.conf gained a new boolean setting Audit= that may be used to + control whether systemd-journald will enable audit during + initialization. + + * when systemd-journald's log stream is broken up into multiple lines + because the PID of the sender changed this is indicated in the + generated log records via the _LINE_BREAK=pid-change field. + + * journalctl's "-o cat" output mode will now show one or more journal + fields specified with --output-fields= instead of unconditionally + MESSAGE=. This is useful to retrieve a very specific set of fields + without any decoration. + + * The sd-journal.h API gained two new functions: + sd_journal_enumerate_available_unique() and + sd_journal_enumerate_available_data() that operate like their + counterparts that lack the _available_ in the name, but skip items + that cannot be read and processed by the local implementation + (i.e. are compressed in an unsupported format or such), + + * coredumpctl gained a new --file= switch, matching the same one in + journalctl: a specific journal file may be specified to read the + coredump data from. + + * coredumps collected by systemd-coredump may now be compressed using + the zstd algorithm. + + * systemd-binfmt gained a new switch --unregister for unregistering all + registered entries at once. This is now invoked automatically at + shutdown, so that binary formats registered with the "F" flag will + not block clean file system unmounting. + + * systemd-notify's --pid= switch gained new values: "parent", "self", + "auto" for controlling which PID to send to the service manager: the + systemd-notify process' PID, or the one of the process invoking it. + + * systemd-logind's Session bus object learnt a new method call + SetType() for temporarily updating the session type of an already + allocated session. This is useful for upgrading tty sessions to + graphical ones once a compositor is invoked. + + * systemd-socket-proxy gained a new switch --exit-idle-time= for + configuring an exit-on-idle time. + + * systemd-repart's --empty= setting gained a new value "create". If + specified a new empty regular disk image file is created under the + specified name. Its size may be specified with the new --size= + option. The latter is also supported without the "create" mode, in + order to grow existing disk image files to the specified size. These + two new options are useful when creating or manipulating disk images + instead of operating on actual block devices. + + * systemd-repart drop-ins now support a new UUID= setting to control + the UUID to assign to a newly created partition. + + * systemd-repart's SizeMin= per-partition parameter now defaults to 10M + instead of 0. + + * systemd-repart's Label= setting now support the usual, simple + specifier expansion. + + * systemd-homed's LUKS backend gained the ability to discard empty file + system blocks automatically when the user logs out. This is enabled + by default to ensure that home directories take minimal space when + logged out but get full size guarantees when logged in. This may be + controlled with the new --luks-offline-discard= switch to homectl. + + * If systemd-homed detects that /home/ is encrypted as a whole it will + now default to the directory or subvolume backends instead of the + LUKS backend, in order to avoid double encryption. The default + storage and file system may now be configured explicitly, too, via + the new /etc/systemd/homed.conf configuration file. + + * systemd-homed now supports unlocking home directories with FIDO2 + security tokens that support the 'hmac-secret' extension, in addition + to the existing support for PKCS#11 security token unlocking + support. Note that many recent hardware security tokens support both + interfaces. The FIDO2 support is accessible via homectl's + --fido2-device= option. + + * homectl's --pkcs11-uri= setting now accepts two special parameters: + if "auto" is specified and only one suitable PKCS#11 security token + is plugged in, its URL is automatically determined and enrolled for + unlocking the home directory. If "list" is specified a brief table of + suitable PKCS#11 security tokens is shown. Similar, the new + --fido2-device= option also supports these two special values, for + automatically selecting and listing suitable FIDO2 devices. + + * The /etc/crypttab tmp option now optionally takes an argument + selecting the file system to use. Moreover, the default is now + changed from ext2 to ext4. + + * There's a new /etc/crypttab option "keyfile-erase". If specified the + key file listed in the same line is removed after use, regardless if + volume activation was successful or not. This is useful if the key + file is only acquired transiently at runtime and shall be erased + before the system continues to boot. + + * There's also a new /etc/crypttab option "try-empty-password". If + specified, before asking the user for a password it is attempted to + unlock the volume with an empty password. This is useful for + installing encrypted images whose password shall be set on first boot + instead of at installation time. + + * systemd-cryptsetup will now attempt to load the keys to unlock + volumes with automatically from files in + /etc/cryptsetup-keys.d/.key and + /run/cryptsetup-keys.d/.key, if any of these files exist. + + * systemd-cryptsetup may now activate Microsoft BitLocker volumes via + /etc/crypttab, during boot. + + * logind.conf gained a new RuntimeDirectoryInodesMax= setting to + control the inode limit for the per-user $XDG_RUNTIME_DIR tmpfs + instance. + + * A new generator systemd-xdg-autostart-generator has been added. It + generates systemd unit files from XDG autostart .desktop files, and + may be used to let the systemd user instance manage services that are + started automatically as part of the desktop session. + + * "bootctl" gained a new verb "reboot-to-firmware" that may be used + to query and change the firmware's 'reboot into firmware' setup flag. + + * systemd-firstboot gained a new switch --kernel-command-line= that may + be used to initialize the /etc/kernel/cmdline file of the image. It + also gained a new switch --root-password-hashed= which is like + --root-password= but accepts a pre-hashed UNIX password as + argument. The new option --delete-root-password may be used to unset + any password for the root user (dangerous!). The --root-shell= switch + may be used to control the shell to use for the root account. A new + --force option may be used to override any already set settings with + the parameters specified on the command line (by default, the tool + will not override what has already been set before, i.e. is purely + incremental). + + * systemd-firstboot gained support for a new --image= switch, which is + similar to --root= but accepts the path to a disk image file, on + which it then operates. + + * A new sd-path.h API has been added to libsystemd. It provides a + simple API for retrieving various search paths and primary + directories for various resources. + + * A new call sd_notify_barrier() has been added to the sd-daemon.h + API. The call will block until all previously sent sd_notify() + messages have been processed by the service manager. This is useful + to remove races caused by a process already having disappeared at the + time a notification message is processed by the service manager, + making correct attribution impossible. The systemd-notify tool will + now make use of this call implicitly, but this can be turned off again + via the new --no-block switch. + + * When sending a file descriptor (fd) to the service manager to keep + track of, using the sd_notify() mechanism, a new parameter FDPOLL=0 + may be specified. If passed the service manager will refrain from + poll()ing on the file descriptor. Traditionally (and when the + parameter is not specified), the service manager will poll it for + POLLHUP or POLLERR events, and immediately close the fds in that + case. + + * The service manager (PID1) gained a new D-Bus method call + SetShowStatus() which may be used to control whether it shall show + boot-time status output on the console. This method has a similar + effect to sending SIGRTMIN+20/SIGRTMIN+21 to PID 1. + + * The sd-bus API gained a number of convenience functions that take + va_list arguments rather than "...". For example, there's now + sd_bus_call_methodv() to match sd_bus_call_method(). Those calls make + it easier to build wrappers that accept variadic arguments and want + to pass a ready va_list structure to sd-bus. + + * sd-bus vtable entries can have a new SD_BUS_VTABLE_ABSOLUTE_OFFSET + flag which alters how the userdata pointer to pass to the callbacks + is determined. When the flag is set, the offset field is converted + as-is into a pointer, without adding it to the object pointer the + vtable is associated with. + + * sd-bus now exposes four new functions: + sd_bus_interface_name_is_valid() + sd_bus_service_name_is_valid() + + sd_bus_member_name_is_valid() + sd_bus_object_path_is_valid() will + validate strings to check if they qualify as various D-Bus concepts. + + * The sd-bus API gained the SD_BUS_METHOD_WITH_ARGS(), + SD_BUS_METHOD_WITH_ARGS_OFFSET() and SD_BUS_SIGNAL_WITH_ARGS() macros + that simplify adding argument names to D-Bus methods and signals. + + * The man pages for the sd-bus and sd-hwdb APIs have been completed. + + * Various D-Bus APIs of systemd daemons now have man pages that + document the methods, signals and properties. + + * The expectations on user/group name syntax are now documented in + detail; documentation on how classic home directories may be + converted into home directories managed by homed has been added; + documentation regarding integration of homed/userdb functionality in + desktops has been added: + + https://systemd.io/USER_NAMES + https://systemd.io/CONVERTING_TO_HOMED + https://systemd.io/USERDB_AND_DESKTOPS + + * Documentation for the on-disk Journal file format has been updated + and has now moved to: + + https://systemd.io/JOURNAL_FILE_FORMAT + + * The interface for containers (https://systemd.io/CONTAINER_INTERFACE) + has been extended by a set of environment variables that expose + select fields from the host's os-release file to the container + payload. Similarly, host's os-release files can be mounted into the + container underneath /run/host. Together, those mechanisms provide a + standardized way to expose information about the host to the + container payload. Both interfaces are implemented in systemd-nspawn. + + * All D-Bus services shipped in systemd now implement the generic + LogControl1 D-Bus API which allows clients to change log level + + target of the service during runtime. + + * Only relevant for developers: the mkosi.default symlink has been + dropped from version control. Please create a symlink to one of the + distribution-specific defaults in .mkosi/ based on your preference. + + Contributions from: 24bisquitz, Adam Nielsen, Alan Perry, Alexander + Malafeev, Amitanand.Chikorde, Alin Popa, Alvin Šipraga, Amos Bird, + Andreas Rammhold, AndreRH, Andrew Doran, Anita Zhang, Ankit Jain, + antznin, Arnaud Ferraris, Arthur Moraes do Lago, Arusekk, Balaji + Punnuru, Balint Reczey, Bastien Nocera, bemarek, Benjamin Berg, + Benjamin Dahlhoff, Benjamin Robin, Chris Down, Chris Kerr, Christian + Göttsche, Christian Hesse, Christian Oder, Ciprian Hacman, Clinton Roy, + codicodi, Corey Hinshaw, Daan De Meyer, Dana Olson, Dan Callaghan, + Daniel Fullmer, Daniel Rusek, Dan Streetman, Dave Reisner, David + Edmundson, David Wood, Denis Pronin, Diego Escalante Urrelo, Dimitri + John Ledkov, dolphrundgren, duguxy, Einsler Lee, Elisei Roca, Emmanuel + Garette, Eric Anderson, Eric DeVolder, Evgeny Vereshchagin, + ExtinctFire, fangxiuning, Ferran Pallarès Roca, Filipe Brandenburger, + Filippo Falezza, Finn, Florian Klink, Florian Mayer, Franck Bui, + Frantisek Sumsal, gaurav, Georg Müller, Gergely Polonkai, Giedrius + Statkevičius, Gigadoc2, gogogogi, Gaurav Singh, gzjsgdsb, Hans de + Goede, Haochen Tong, ianhi, ignapk, Jakov Smolic, James T. Lee, Jan + Janssen, Jan Klötzke, Jan Palus, Jay Burger, Jeremy Cline, Jérémy + Rosen, Jian-Hong Pan, Jiri Slaby, Joel Shapiro, Joerg Behrmann, Jörg + Thalheim, Jouke Witteveen, Kai-Heng Feng, Kenny Levinsen, Kevin + Kuehler, Kumar Kartikeya Dwivedi, layderv, laydervus, Lénaïc Huard, + Lennart Poettering, Lidong Zhong, Luca Boccassi, Luca BRUNO, Lucas + Werkmeister, Lukas Klingsbo, Lukáš Nykrýn, Łukasz Stelmach, Maciej + S. Szmigiero, MadMcCrow, Marc-André Lureau, Marcel Holtmann, Marc + Kleine-Budde, Martin Hundebøll, Matthew Leeds, Matt Ranostay, Maxim + Fomin, MaxVerevkin, Michael Biebl, Michael Chapman, Michael Gubbels, + Michael Marley, Michał Bartoszkiewicz, Michal Koutný, Michal Sekletár, + Mike Gilbert, Mike Kazantsev, Mikhail Novosyolov, ml, Motiejus Jakštys, + nabijaczleweli, nerdopolis, Niccolò Maggioni, Niklas Hambüchen, Norbert + Lange, Paul Cercueil, pelzvieh, Peter Hutterer, Piero La Terza, Pieter + Lexis, Piotr Drąg, Rafael Fontenelle, Richard Petri, Ronan Pigott, Ross + Lagerwall, Rubens Figueiredo, satmandu, Sean-StarLabs, Sebastian + Jennen, sterlinghughes, Surhud More, Susant Sahani, szb512, Thomas + Haller, Tobias Hunger, Tom, Tomáš Pospíšek, Tomer Shechner, Tom Hughes, + Topi Miettinen, Tudor Roman, Uwe Kleine-König, Valery0xff, Vito Caputo, + Vladimir Panteleev, Vladyslav Tronko, Wen Yang, Yegor Vialov, Yigal + Korman, Yi Gao, YmrDtnJu, Yuri Chornoivan, Yu Watanabe, Zbigniew + Jędrzejewski-Szmek, Zhu Li, Дамјан Георгиевски, наб + + – Warsaw, 2020-07-30 + CHANGES WITH 245: * A new tool "systemd-repart" has been added, that operates as an @@ -171,8 +775,6 @@ CHANGES WITH 245: * systemd-sysusers gained support for creating users with the primary group named differently than the user. - * systemd-resolved's DNS-over-TLS support gained SNI validation. - * systemd-growfs (i.e. the x-systemd.growfs mount option in /etc/fstab) gained support for growing XFS partitions. Previously it supported only ext4 and btrfs partitions. @@ -270,7 +872,7 @@ CHANGES WITH 245: such files in version 243. * systemd-logind will now validate access to the operation of changing - the virtual terminal via a PolicyKit action. By default, only users + the virtual terminal via a polkit action. By default, only users with at least one session on a local VT are granted permission. * When systemd sets up PAM sessions that invoked service processes @@ -373,7 +975,7 @@ CHANGES WITH 244: of the PAM session, for example for time-limited logins. * A new @pkey system call group is now defined to make it easier to - whitelist memory protection syscalls for containers and services + allow-list memory protection syscalls for containers and services which need to use them. * systemd-udevd: removed the 30s timeout for killing stale workers on @@ -390,10 +992,10 @@ CHANGES WITH 244: * udev now provides a program (fido_id) that identifies FIDO CTAP1 ("U2F")/CTAP2 security tokens based on the usage declared in their report and descriptor and outputs suitable environment variables. - This replaces the externally maintained whitelists of all known + This replaces the externally maintained allow lists of all known security tokens that were used previously. - * Automatically generated autosuspend udev rules for whitelisted + * Automatically generated autosuspend udev rules for allow-listed devices have been imported from the Chromium OS project. This should improve power saving with many more devices. @@ -501,8 +1103,8 @@ CHANGES WITH 244: configuration time using the -Dservice-watchdog= setting. If set to empty, the watchdogs will be disabled. - * systemd-resolved validates IP addresses in certificates now when GnuTLS - is being used. + * systemd-resolved validates IP addresses in certificates now when GnuTLS + is being used. * libcryptsetup >= 2.0.1 is now required. @@ -684,10 +1286,10 @@ CHANGES WITH 243: the IO accounting data is included in the resource log message generated whenever a unit stops. - * Units may now configure an explicit time-out to wait for when killed + * Units may now configure an explicit timeout to wait for when killed with SIGABRT, for example when a service watchdog is hit. Previously, - the regular TimeoutStopSec= time-out was applied in this case too — - now a separate time-out may be set using TimeoutAbortSec=. + the regular TimeoutStopSec= timeout was applied in this case too — + now a separate timeout may be set using TimeoutAbortSec=. * Services may now send a special WATCHDOG=trigger message with sd_notify() to trigger an immediate "watchdog missed" event, and thus @@ -717,7 +1319,7 @@ CHANGES WITH 243: * If processes terminated during the last phase of shutdown do not exit quickly systemd will now show their names after a short time, to make - debugging easier. After a longer time-out they are forcibly killed, + debugging easier. After a longer timeout they are forcibly killed, as before. * journalctl (and the other tools that display logs) will now highlight @@ -760,7 +1362,7 @@ CHANGES WITH 243: * systemd-networkd's DHCPv4 support now understands a new MaxAttempts= option for configuring the maximum number of DHCP lease requests. It - also learnt a new BlackList= option for blacklisting DHCP servers (a + also learnt a new BlackList= option for deny-listing DHCP servers (a similar setting has also been added to the IPv6 RA client), as well as a SendRelease= option for configuring whether to send a DHCP RELEASE message when terminating. @@ -988,7 +1590,7 @@ CHANGES WITH 243: space if there are multiple devices with the highest priority. * /etc/crypttab support has learnt a new keyfile-timeout= per-device - option that permits selecting the timout how long to wait for a + option that permits selecting the timeout how long to wait for a device with an encryption key before asking for the password. * IOWeight= has learnt to properly set the IO weight when using the @@ -1992,12 +2594,12 @@ CHANGES WITH 239: any relevant symlinks both in /run and /etc. * Note that all long-running system services shipped with systemd will - now default to a system call whitelist (rather than a blacklist, as + now default to a system call allow list (rather than a deny list, as before). In particular, systemd-udevd will now enforce one too. For most cases this should be safe, however downstream distributions which disabled sandboxing of systemd-udevd (specifically the MountFlags= setting), might want to disable this security feature - too, as the default whitelisting will prohibit all mount, swap, + too, as the default allow-listing will prohibit all mount, swap, reboot and clock changing operations from udev rules. * sd-boot acquired new loader configuration settings to optionally turn @@ -2025,7 +2627,7 @@ CHANGES WITH 239: lookup is likely to trigger nss-ldap which in turn might use NSS to ask systemd-resolved for hostname lookups. This will hence result in a deadlock: a user name lookup in order to start - systemd-resolved.service will result in a host name lookup for which + systemd-resolved.service will result in a hostname lookup for which systemd-resolved.service needs to be started already. There are multiple ways to work around this problem: pre-allocate the "systemd-resolve" user on such systems, so that nss-ldap won't be @@ -2994,7 +3596,7 @@ CHANGES WITH 235: A/AAAA resource record for the "_gateway" hostname, pointing to the current default IP gateway. Previously it did that for the "gateway" name, hampering adoption, as some distributions wanted to leave that - host name open for local use. The old behaviour may still be + hostname open for local use. The old behaviour may still be requested at build time. * systemd-networkd's [Address] section in .network files gained a new @@ -3025,7 +3627,7 @@ CHANGES WITH 235: * systemd-nspawn gained support for a new --system-call-filter= command line option for adding and removing entries in the default system call filter it applies. Moreover systemd-nspawn has been changed to - implement a system call whitelist instead of a blacklist. + implement a system call allow list instead of a deny list. * systemd-run gained support for a new --pipe command line option. If used the STDIN/STDOUT/STDERR file descriptors passed to systemd-run @@ -3513,7 +4115,7 @@ CHANGES WITH 233: that is removed when the container dies. Specifically, if the source directory is specified as empty string this mechanism is selected. An example usage is --overlay=+/var::/var, which creates an overlay - mount based on the original /var contained in the image, overlayed + mount based on the original /var contained in the image, overlaid with a temporary directory in the host's /var/tmp. This way changes to /var are automatically flushed when the container shuts down. @@ -4335,7 +4937,7 @@ CHANGES WITH 230: again don't consider turning this on in your stable, LTS or production release just yet. (Note that you have to enable nss-resolve in /etc/nsswitch.conf, to actually use systemd-resolved - and its DNSSEC mode for host name resolution from local + and its DNSSEC mode for hostname resolution from local applications.) * systemd-resolve conveniently resolves DANE records with the --tlsa @@ -5738,11 +6340,10 @@ CHANGES WITH 220: fsck's progress report to an AF_UNIX socket in the file system. - * udev will no longer create device symlinks for all block - devices by default. A blacklist for excluding special block - devices from this logic has been turned into a whitelist - that requires picking block devices explicitly that require - device symlinks. + * udev will no longer create device symlinks for all block devices by + default. A deny list for excluding special block devices from this + logic has been turned into a allow list that requires picking block + devices explicitly that require device symlinks. * A new (currently still internal) API sd-device.h has been added to libsystemd. This modernized API is supposed to @@ -6153,14 +6754,14 @@ CHANGES WITH 218: for a unit, as declared in the (usually vendor-supplied) system preset files. - * nss-myhostname will now resolve the single-label host name + * nss-myhostname will now resolve the single-label hostname "gateway" to the locally configured default IP routing gateways, ordered by their metrics. This assigns a stable name to the used gateways, regardless which ones are currently configured. Note that the name will only be resolved after all other name sources (if nss-myhostname is configured properly) and should hence not negatively impact - systems that use the single-label host name "gateway" in + systems that use the single-label hostname "gateway" in other contexts. * systemd-inhibit now allows filtering by mode when listing @@ -6500,7 +7101,7 @@ CHANGES WITH 217: * Calendar time specifications in .timer units now also understand the strings "semi-annually", "quarterly" and "minutely" as shortcuts (in addition to the preexisting - "anually", "hourly", ...). + "annually", "hourly", ...). * systemd-tmpfiles will now correctly create files in /dev at boot which are marked for creation only at boot. It is @@ -7588,7 +8189,7 @@ CHANGES WITH 210: reported by uname()'s "machine" field. * systemd-networkd now supports matching on the system - virtualization, architecture, kernel command line, host name + virtualization, architecture, kernel command line, hostname and machine ID. * logind is now a lot more aggressive when suspending the @@ -7631,11 +8232,11 @@ CHANGES WITH 210: Wikipedia. We explicitly document which base applies for each configuration option. - * The DeviceAllow= setting in unit files now supports a syntax - to whitelist an entire group of devices node majors at once, - based on the /proc/devices listing. For example, with the - string "char-pts", it is now possible to whitelist all - current and future pseudo-TTYs at once. + * The DeviceAllow= setting in unit files now supports a syntax to + allow-list an entire group of devices node majors at once, based on + the /proc/devices listing. For example, with the string "char-pts", + it is now possible to allow-list all current and future pseudo-TTYs + at once. * sd-event learned a new "post" event source. Event sources of this type are triggered by the dispatching of any event @@ -7906,12 +8507,12 @@ CHANGES WITH 209: example, a line that creates /run/nologin). * A new API "sd-resolve.h" has been added which provides a simple - asynchronous wrapper around glibc NSS host name resolution + asynchronous wrapper around glibc NSS hostname resolution calls, such as getaddrinfo(). In contrast to glibc's getaddrinfo_a(), it does not use signals. In contrast to most other asynchronous name resolution libraries, this one does not reimplement DNS, but reuses NSS, so that alternate - host name resolution systems continue to work, such as mDNS, + hostname resolution systems continue to work, such as mDNS, LDAP, etc. This API is based on libasyncns, but it has been cleaned up for inclusion in systemd. @@ -9695,7 +10296,7 @@ CHANGES WITH 190: when he over-mounts a non-empty directory. * There are new specifiers that are resolved in unit files, - for the host name (%H), the machine ID (%m) and the boot ID + for the hostname (%H), the machine ID (%m) and the boot ID (%b). Contributions from: Allin Cottrell, Auke Kok, Brandon Philips, @@ -9878,9 +10479,9 @@ CHANGES WITH 187: * journalctl gained the new "--header" switch to introspect header data of journal files. - * A new setting SystemCallFilters= has been added to services - which may be used to apply blacklists or whitelists to - system calls. This is based on SECCOMP Mode 2 of Linux 3.5. + * A new setting SystemCallFilters= has been added to services which may + be used to apply deny lists or allow lists to system calls. This is + based on SECCOMP Mode 2 of Linux 3.5. * nspawn gained a new --link-journal= switch (and quicker: -j) to link the container journal with the host. This makes it diff --git a/README b/README index b2c8d2841..558b8d919 100644 --- a/README +++ b/README @@ -35,6 +35,7 @@ LICENSE: REQUIREMENTS: Linux kernel >= 3.13 Linux kernel >= 4.2 for unified cgroup hierarchy support + Linux kernel >= 5.4 for signed Verity images support Kernel Config Options: CONFIG_DEVTMPFS @@ -102,6 +103,9 @@ REQUIREMENTS: CONFIG_EFIVAR_FS CONFIG_EFI_PARTITION + Required for signed Verity images support: + CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG + We recommend to turn off Real-Time group scheduling in the kernel when using systemd. RT group scheduling effectively makes RT scheduling unavailable for most userspace, since it @@ -144,12 +148,13 @@ REQUIREMENTS: libblkid >= 2.24 (from util-linux) (optional) libkmod >= 15 (optional) PAM >= 1.1.2 (optional) - libcryptsetup (optional) + libcryptsetup (optional), >= 2.3.0 required for signed Verity images support libaudit (optional) libacl (optional) libselinux (optional) liblzma (optional) liblz4 >= 1.3.0 / 130 (optional) + libzstd >= 1.4.0 (optional) libgcrypt (optional) libqrencode (optional) libmicrohttpd (optional) @@ -257,19 +262,19 @@ USERS AND GROUPS: NSS: systemd ships with four glibc NSS modules: - nss-myhostname resolves the local hostname to locally - configured IP addresses, as well as "localhost" to - 127.0.0.1/::1. + nss-myhostname resolves the local hostname to locally configured IP + addresses, as well as "localhost" to 127.0.0.1/::1. - nss-resolve enables DNS resolution via the systemd-resolved - DNS/LLMNR caching stub resolver "systemd-resolved". + nss-resolve enables DNS resolution via the systemd-resolved DNS/LLMNR + caching stub resolver "systemd-resolved". nss-mymachines enables resolution of all local containers registered - with machined to their respective IP addresses. It also maps UID/GIDs - ranges used by containers to useful names. + with machined to their respective IP addresses. - nss-systemd enables resolution of all dynamically allocated service - users. (See the DynamicUser= setting in unit files.) + nss-systemd enables resolution of users/group registered via the + User/Group Record Lookup API (https://systemd.io/USER_GROUP_API/), + including all dynamically allocated service users. (See the + DynamicUser= setting in unit files.) To make use of these NSS modules, please add them to the "hosts:", "passwd:" and "group:" lines in /etc/nsswitch.conf. The "resolve" @@ -278,8 +283,8 @@ NSS: The four modules should be used in the following order: - passwd: compat mymachines systemd - group: compat mymachines systemd + passwd: compat systemd + group: compat systemd hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname SYSV INIT.D SCRIPTS: diff --git a/README.md b/README.md index 0274715e8..f836d812b 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,21 @@ System and Service Manager Count of open pull requests over time [![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)
[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)
-[![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=systemd&branch=master)](https://app.fuzzit.dev/orgs/systemd/dashboard)
-[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
+[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#systemd)
+[![CIFuzz](https://github.com/systemd/systemd/workflows/CIFuzz/badge.svg)](https://github.com/systemd/systemd/actions)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)
[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)
[![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)
[![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/)
-[![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master) +[![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master)
+[![Fossies codespell report](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.svg)](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html)
+[![Packaging status](https://repology.org/badge/tiny-repos/systemd.svg)](https://repology.org/project/systemd/versions) ## Details -General information about systemd can be found in the [systemd Wiki](https://www.freedesktop.org/wiki/Software/systemd). +Most documentation is available on [systemd's web site](https://systemd.io/). + +Assorted, older, general information about systemd can be found in the [systemd Wiki](https://www.freedesktop.org/wiki/Software/systemd). Information about build requirements is provided in the [README file](README). diff --git a/TODO b/TODO index 9bba36fe6..58fdc45e1 100644 --- a/TODO +++ b/TODO @@ -8,8 +8,6 @@ External: * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros. -* wiki: update journal format documentation for lz4 additions - Janitorial Clean-ups: * Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again @@ -19,13 +17,164 @@ Janitorial Clean-ups: Features: -* cryptsetup/homed: also support FIDO2 HMAC password logic for unlocking - devices. (see: https://github.com/mjec/fido2-hmac-secret) +* nspawn: move "incoming mount" directory to /run/host, move "inaccessible" + nodes to /run/host, move notify socket (for sd_notify() between payload and + container manager) + +* cryptsetup: if keyfile specified in crypttab is AF_UNIX socket, connect to it + and read from it (like we do elsewhere with READ_FULL_FILE_CONNECT_SOCKET) + +* repart: support setting up dm-integrity with HMAC + +* add /etc/integritytab, to support dm-integrity setups. In particular those + with HMAC as hash function, so that we can have a protected /home without + encryption (leaving encryption to the individual dirs/homed). + +* complement root=, rootflags=, rootfstype= with rootsubdir= which allows + mounting a subdir of the root fs as actual root. This can be used as + fstype-agnostic version of btrfs' rootflags=subvol=foobar. + +* add --copy-from and --copy-to command to systemd-dissect which copies stuff + in and out of a disk image + +* Support ProtectProc= or so, using: https://patchwork.kernel.org/cover/11310197/ + +* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it + +* build short web pages out of each catalog entry, build them along with man + pages, and include hyperlinks to them in the journal output + +* machined: add API to acquire UID range. add API to mount/dissect loopback + file. Both protected by PK. Then make nspawn use these APIs to run + unprivileged containers. i.e. push the truly privileged bits into machined, + so that the client side can remain entirely unprivileged, with SUID or + anything like that. + +* journald: do journal file writing out-of-process, with one writer process per + client UID, so that synthetic hash table collisions can slow down a specific + user's journal stream down but not the others. + +* add "throttling" to sd-event event sources: optionally, when we wake up too + often for one, let's turn it off entirely for a while. Use that for the + /proc/self/mountinfo logic. + +* move our systemd-user PAM snippet to /usr/, which PAM appears to support + these days + +* nspawn: support time namespaces + +* systemd-firstboot: make sure to always use chase_symlinks() before + reading/writing files + +* add ConditionSecurity=tpm2 + +* Remove any support for booting without /usr pre-mounted in the initrd entirely. + Update INITRD_INTERFACE.md accordingly. + +* pid1: Move to tracking of main pid/control pid of units per pidfd + +* pid1: support new clone3() fork-into-cgroup feature + +* pid1: also remove PID files of a service when the service starts, not just + when it exits + +* make us use dynamically fewer deps for containers in general purpose distros: + o turn into dlopen() deps: + - pcre2 (always) — irrelevant on Fedora, since dep by + libselinux, but should benefit Debian + - libpwquality (always) - only relevant for homed, and maybe soon + firstboot + - elfutils (always) + - p11-kit-trust (always) + - kmod-libs (only when called from PID 1) + - cryptsetup-libs (only in RootImage= handling in PID 1, but not in systemd-cryptsetup) + - similar: libblkid + - libpam (only when called from PID 1) + - bzip2, xz, lz4 (always — gzip and zstd should probably stay static deps the way they are, + since they are so basic and our defaults) + o move into separate libsystemd-shared-iptables.so .so + - iptables-libs (only used by nspawn + networkd) + +* seccomp: when SystemCallArchitectures=native is set then don't install any + other seccomp filters for any of the other archs, in order to reduce the + number of seccomp filters we install needlessly. + +* seccomp: maybe use seccomp_merge() to merge our filters per-arch if we can. + Apparently kernel performance is much better with fewer larger seccomp + filters than with more smaller seccomp filters. + +* systemd-path: add ESP and XBOOTLDR path. Add "private" runtime/state/cache dir enum, + mapping to $RUNTIME_DIRECTORY, $STATE_DIRECTORY and such + +* make "systemd-dissect" an official supported tool, i.e. move to /usr/bin/ and + provide man page. Given that we now have a tool that can generate images like + this, it's useful to have one that can dump contents of them, too. + +* All tools that support --root= should also learn --image= so that they can + operate on disk images directly. Specifically: bootctl, tmpfiles, sysusers, + systemctl, repart, journalctl, coredumpctl. (Already done: systemd-nspawn, + systemd-firstboot) + +* seccomp: by default mask x32 ABI system wide on x86-64. it's on its way out + +* 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. + +* per-service credential system. Specifically: add LoadCredential= (for loading + cred from file), AcquireCredential= (for asking user for cred, via + ask-password), PassCredential= (for passing on credential systemd itself + got). Then, place credentials in a per-service, immutable ramfs instance (so + that it cannot be swapped out), destroy after use. Also pass via keyring + (with graceful fallback to cover for containers). Define CredentialPath= for + defining subdir of /run/credentials/ where to place it. Set $CREDENTIAL_PATH + env var for services to the result. Also pass via fd passing (optionally). + +* homed: add native recovery key support. use 48 lowercase modhex characters + (192bit), show qr code of it, include pattern expression in user record. + +* homed: introduce "degraded" state for home directories that weren't cleanly + unmounted (use xattr we add and remove on the loop back file) + +* homed: during login resize fs automatically towards size goal. Specifically, + resize to diskSize if possible, but leave a certain amount (configured by a + new value diskLeaveFreeSize) of space free on the backing fs. + +* homed: permit multiple user record signing keys to be used locally, and pick + the right one for signing records automatically depending on a pre-existing + signature + +* homed: add a way to "adopt" a home directory, i.e. strip foreign signatures + and insert a local signature instead. + +* homed: as an extension to the directory+subvolume backend: if located on + especially marked fs, then sync down password into LUKS header of that fs, + and always verify passwords against it too. Bootstrapping is a problem + though: if no one is logged in (or no other user even exists yet), how do you + unlock the volume in order to create the first user and add the first pw. + +* homed: support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt + +* homed: maybe pre-create ~/.cache as subvol so that it can have separate quota + easily? + +* busctl: maybe expose a verb "ping" for pinging a dbus service to see if it + exists and responds. + +* when systemd-nspawn and suchlike dissect an OS image, and there are multiple + root partitions, do an strverscmp() on the partition label and boot + first. That is inspired how sd-boot figures out which kernel to boot, and + thus allows defining OS images which can be A/B updated and we default to the + newest version automatically, both in nspawn and in sd-boot + +* cryptsetup: support FIDO2 tokens for deriving keys (i.e. do what homed can do + also in plain cryptsetup) * systemd-gpt-auto should probably set x-systemd.growfs on the mounts it creates -* homed/userdb: distuingish passwords and recovery keys in the records, since +* homed/userdb: distinguish passwords and recovery keys in the records, since we probably want to use different PBKDF algorithms/settings for them: passwords have low entropy but recovery keys should have good entropy key hence we can make them quicker to work. @@ -35,12 +184,13 @@ Features: - 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 +* Maybe add a separate GPT partition type to the discoverable partition spec + for "hibernate" partitions, that are exactly like swap partitions but only + activated right before hibernation and thus never used for regular swapping. + * by default, in systemd --user service bump the OOMAdjust to 100, as privs allow so that systemd survives -* honour specifiers in unit files that resolve to some very basic - /etc/os-release data, such as ID, VERSION_ID, BUILD_ID, VARIANT_ID. - * cryptsetup: allow encoding key directly in /etc/crypttab, maybe with a "base64:" prefix. Useful in particular for pkcs11 mode. @@ -48,9 +198,10 @@ Features: systemd-makefs.service instead. * socket units: allow creating a udev monitor socket with ListenDevices= or so, - with matches, then actviate app thorugh that passing socket oveer + with matches, then activate app through that passing socket over -* unify on openssl: +* unify on openssl (as soon as OpenSSL 3.0 is out, and the Debian license + confusion is gone) - port sd_id128_get_machine_app_specific() over from khash - port resolved over from libgcrypt (DNSSEC code) - port journald + fsprg over from libgcrypt @@ -72,17 +223,10 @@ Features: that the device paths stay the same, regardless if crypto is used or not. * systemd-repart: by default generate minimized partition tables (i.e. tables - that only covere the space actually used, excluding any free space at the + that only cover the space actually used, excluding any free space at the end), in order to maximize dd'ability. Requires libfdisk work, see https://github.com/karelzak/util-linux/issues/907 -* systemd-repart: optionally, allow specifiying a path to initialize new - partitions from, i.e. an fs image file or a source device node. This would - then turn systemd-repart into a simple installer: with a few .repart files - you could replicate the host system on another device. a full installer would - then be: "systemd-repart /dev/sda && bootctl install /dev/sda && - systemd-firstboot --image= …" - * systemd-repart: MBR partition table support. Care needs to be taken regarding Type=, so that partition definitions can sanely apply to both the GPT and the MBR case. Idea: accept syntax "Type=gpt:home mbr:0x83" for setting the types @@ -98,26 +242,37 @@ Features: * systemd-repart: allow managing the gpt read-only partition flag + auto-mount flag +* systemd-repart: allow boolean option that ensures that if existing partition + doesn't exist within the configured size bounds the whole command fails. This + is useful to implement ESP vs. XBOOTLDR schemes in installers: have one set + of repart files for the case where ESP is large enough and one where it isn't + and XBOOTLDR is added in instead. Then apply the former first, and if it + fails to apply use the latter. + +* systemd-repart: add per-partition option to never reuse existing partition + and always create anew even if matching partition already exists. + +* systemd-repart: add per-partition option to fail if partition already exist, + i.e. is not added new. Similar, add option to fail if partition does not exist yet. + +* systemd-repart: add --size=auto for generating/resizing images of minimal + size, i.e. where the image file is sized exactly as large as necessary taking + SizeMin= into account, but not a single byte larger. + * systemd-repart: allow disabling growing of specific partitions, or making them (think ESP: we don't ever want to grow it, since we cannot resize vfat) -* systemd-repart: add specifier expansion, add especifier that refers to root - device node of current system, /usr device node, and matching verity, so that - an installer can be made a "copy" installer of the booted OS - * systemd-repart: make it a static checker during early boot for existence and absence of other partitions for trusted boot environments -* systemd-repart: when no configuration is found, exit early do not check - partition table, so that it is safe to run in the initrd on any system +* userdb: allow username prefix searches in varlink API, allow realname and + realname substr searches in varlink API -* systemd-repart: allow config of partition uuid - -* userdb: allow username prefix searches in varlink API +* userdb: allow uid/gid range checks * userdb: allow existence checks -* pid: activation by journal search expression +* pid1: activation by journal search expression * when switching root from initrd to host, set the machine_id env var so that if the host has no machine ID set yet we continue to use the random one the @@ -176,14 +331,11 @@ Features: * systemd-firstboot: teach it dissector magic, so that you can point it to some disk image and it will just set everything in it all behind the scenes. -* systemd-firstboot: add --force mode that replaces existing configuration. - * We should probably replace /var/log/README, /etc/rc.d/README with symlinks that are linked to these places instead of copied. After all they are constant vendor data. -* maybe add kernel cmdline params: 1) to force first-boot mode + 2) to force - random seed crediting +* maybe add kernel cmdline params: to force random seed crediting * nspawn: on cgroupsv1 issue cgroup empty handler process based on host events, so that we make cgroup agent logic safe @@ -197,11 +349,9 @@ Features: * homed: - when user tries to log into record signed by unrecognized key, automatically add key to our chain after polkit auth - - hook up machined/nspawn users with a varlink user query interface - rollback when resize fails mid-operation - GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid) - resize on login? - - fstrim on logout? - shrink fs on logout? - update LUKS password on login if we find there's a password that unlocks the JSON record but not the LUKS device. - create on activate? @@ -210,8 +360,9 @@ Features: beefing up logind to make pam session close hook synchronous and wait until systemd --user is shut down. - logind: maybe keep a "busy fd" as long as there's a non-released session around or the user@.service - - maybe make automatic, read-only, time-based reflink-copies of LUKS disk images (think: time machine) - - distuingish destroy / remove (i.e. currently we can unregister a user, unregister+remove their home directory, but not just remove their home directory) + - maybe make automatic, read-only, time-based reflink-copies of LUKS disk + images (and btrfs snapshots of subvolumes) (think: time machine) + - distinguish destroy / remove (i.e. currently we can unregister a user, unregister+remove their home directory, but not just remove their home directory) - in systemd's PAMName= logic: query passwords with ssh-askpassword, so that we can make "loginctl set-linger" mode work - fingerprint authentication, pattern authentication, … - make sure "classic" user records can also be managed by homed @@ -226,6 +377,9 @@ Features: - make slice for users configurable (requires logind rework) - logind: populate auto-login list bus property from PKCS#11 token - when determining state of a LUKS home directory, check DM suspended sysfs file + - introduce API for "making room", that grows/shrinks home directory + according to elastic parameters, discards blocks, and removes additional snapshots. Call it + either from UI when disk space gets low * introduce a new per-process uuid, similar to the boot id, the machine id, the invocation id, that is derived from process creds, specifically a hashed @@ -802,6 +956,10 @@ Features: * teach ConditionKernelCommandLine= globs or regexes (in order to match foobar={no,0,off}) +* Add ConditionDirectoryNotEmpty= handle non-absoute paths as a search path or add + ConditionConfigSearchPathNotEmpty= or different syntax? See the discussion starting at + https://github.com/systemd/systemd/pull/15109#issuecomment-607740136. + * BootLoaderSpec: Clarify that the kernel has to be in $BOOT. Clarify that the boot loader should be installed to the ESP. Define a way how an installer can figure out whether a BLS compliant boot loader @@ -857,6 +1015,7 @@ Features: make assumptions about their slice anymore. - follow PropertiesChanged state more closely, to deal with quick logouts and relogins + - (optionally?) spawn seat-manager@$SEAT.service whenever a seat shows up that as CanGraphical set * journal: - consider introducing implicit _TTY= + _PPID= + _EUID= + _EGID= + _FSUID= + _FSGID= fields @@ -874,7 +1033,7 @@ Features: - journal: add a setgid "systemd-journal" utility to invoke from libsystemd-journal, which passes fds via STDOUT and does PK access - journactl: support negative filtering, i.e. FOOBAR!="waldo", and !FOOBAR for events without FOOBAR. - - journal: store timestamp of journal_file_set_offline() int he header, + - journal: store timestamp of journal_file_set_offline() in the header, so it is possible to display when the file was last synced. - journal-send.c, log.c: when the log socket is clogged, and we drop, count this and write a message about this when it gets unclogged again. - journal: find a way to allow dropping history early, based on priority, other rules @@ -916,6 +1075,7 @@ Features: them via machined, and also watch containers coming and going. Benefit: nspawn --ephemeral would start working nicely with the journal. - assign MESSAGE_ID to log messages about failed services + - check if loop in decompress_blob_xz() is necessary * add a test if all entries in the catalog are properly formatted. (Adding dashes in a catalog entry currently results in the catalog entry @@ -1131,18 +1291,15 @@ Features: * networkd: - add more keys to [Route] and [Address] sections - add support for more DHCPv4 options (and, longer term, other kinds of dynamic config) - - add proper initrd support (in particular generate .network/.link files based on /proc/cmdline) - add reduced [Link] support to .network files - - add Scope= parsing option for [Network] - properly handle routerless dhcp leases - work with non-Ethernet devices - - add support for more bond options - dhcp: do we allow configuring dhcp routes on interfaces that are not the one we got the dhcp info from? - the DHCP lease data (such as NTP/DNS) is still made available when a carrier is lost on a link. It should be removed instantly. - expose in the API the following bits: - - option 15, domain name and/or option 119, search list - - option 12, host name and/or option 81, fqdn + - option 15, domain name + - option 12, hostname and/or option 81, fqdn - option 123, 144, geolocation - option 252, configure http proxy (PAC/wpad) - provide a way to define a per-network interface default metric value @@ -1150,11 +1307,9 @@ Features: - allow Name= to be specified repeatedly in the [Match] section. Maybe also support Name=foo*|bar*|baz ? - duplicate address check for static IPs (like ARPCHECK in network-scripts) - - allow DUID/IAID to be customized, see issue #394. - whenever uplink info changes, make DHCP server send out FORCERENEW -* networkd-wait-online: - - make operstates to wait for configurable? +* Figure out how to do unittests of networkd's state serialization * dhcp: - figure out how much we can increase Maximum Message Size @@ -1179,20 +1334,14 @@ External: - natively watch for dbus-*.service symlinks (PENDING) - teach dbus to activate all services it finds in /etc/systemd/services/org-*.service -* fix alsa mixer restore to not print error when no config is stored - * make cryptsetup lower --iter-time -* patch kernel for xattr support in /dev, /proc/, /sys? - * kernel: add device_type = "fb", "fbcon" to class "graphics" * /usr/bin/service should actually show the new command line * fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus= -* fedora: F20: go timer units all the way, leave cron.daily for cron - * neither pkexec nor sudo initialize environ[] from the PAM environment? * fedora: update policy to declare access mode and ownership of unit files to root:root 0644, and add an rpmlint check for it diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index 3342d5942..056df00de 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -302,7 +302,8 @@ shut down. Subject: DNSSEC mode has been turned off, as server doesn't support it Defined-By: systemd Support: %SUPPORT_URL% -Documentation: man:systemd-resolved.service(8) resolved.conf(5) +Documentation: man:systemd-resolved.service(8) +Documentation: man:resolved.conf(5) The resolver service (systemd-resolved.service) has detected that the configured DNS server does not support DNSSEC, and DNSSEC validation has been @@ -417,6 +418,7 @@ Note that the memory pressure might or might not have been caused by @UNIT@. Subject: Accepting user/group name @USER_GROUP_NAME@, which does not match strict user/group name rules. Defined-By: systemd Support: %SUPPORT_URL% +Documentation: https://systemd.io/USER_NAMES The user/group name @USER_GROUP_NAME@ has been specified, which is accepted according the relaxed user/group name rules, but does not qualify under the @@ -432,6 +434,86 @@ characters; names not valid UTF-8; names with leading or trailing whitespace; the strings "." or ".."; fully numeric strings, or strings beginning in a hyphen and otherwise fully numeric. -For further details on strict and relaxed user/group name rules, see: +-- 1b3bb94037f04bbf81028e135a12d293 +Subject: Failed to generate valid unit name from path '@MOUNT_POINT@'. +Defined-By: systemd +Support: %SUPPORT_URL% -https://systemd.io/USER_NAMES +The following mount point path could not be converted into a valid .mount +unit name: + + @MOUNT_POINT@ + +Typically this means that the path to the mount point is longer than allowed +for valid unit names. + +systemd dynamically synthesizes .mount units for all mount points appearing on +the system. For that a simple escaping algorithm is applied: the absolute path +name is used, with all "/" characters replaced by "-" (the leading one is +removed). Moreover, any non-alphanumeric characters (as well as any of ":", +"-", "_", ".", "\") are replaced by "\xNN" where "NN" is the hexadecimal code +of the character. Finally, ".mount" is suffixed. The resulting string must be +under 256 characters in length to be a valid unit name. This restriction is +made in order for all unit names to also be suitable as file names. If a mount +point appears that — after escaping — is longer than this limit it cannot be +mapped to a unit. In this case systemd will refrain from synthesizing a unit +and cannot be used to manage the mount point. It will not appear in the service +manager's unit table and thus also not be torn down safely and automatically at +system shutdown. + +It is generally recommended to avoid such overly long mount point paths, or — +if used anyway – manage them independently of systemd, i.e. establish them as +well as tear them down automatically at system shutdown by other software. + +-- b480325f9c394a7b802c231e51a2752c +Subject: Special user @OFFENDING_USER@ configured, this is not safe! +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: https://systemd.io/UIDS-GIDS + +The unit @UNIT@ is configured to use User=@OFFENDING_USER@. + +This is not safe. The @OFFENDING_USER@ user's main purpose on Linux-based +operating systems is to be the owner of files that otherwise cannot be mapped +to any local user. It's used by the NFS client and Linux user namespacing, +among others. By running a unit's processes under the identity of this user +they might possibly get read and even write access to such files that cannot +otherwise be mapped. + +It is strongly recommended to avoid running services under this user identity, +in particular on systems using NFS or running containers. Allocate a user ID +specific to this service, either statically via systemd-sysusers or dynamically +via the DynamicUser= service setting. + +-- 1c0454c1bd2241e0ac6fefb4bc631433 +Subject: systemd-udev-settle.service is deprecated. +Defined-By: systemd +Support: %SUPPORT_URL% + +Usage of the systemd service unit systemd-udev-settle.service is deprecated. It +inserts artificial delays into the boot process without providing the +guarantees other subsystems traditionally assumed it provides. Relying on this +service is racy, and it is generally a bug to make use of it and depend on it. + +Traditionally, this service's job was to wait until all devices a system +possesses have been fully probed and initialized, delaying boot until this +phase is completed. However, today's systems and hardware generally don't work +this way anymore, hardware today may show up any time and take any time to be +probed and initialized. Thus, in the general case, it's no longer possible to +correctly delay boot until "all devices" have been processed, as it is not +clear what "all devices" means and when they have been found. This is in +particular the case if USB hardware or network-attached hardware is used. + +Modern software that requires some specific hardware (such as a network device +or block device) to operate should only wait for the specific devices it needs +to show up, and otherwise operate asynchronously initializing devices as they +appear during boot and during runtime without delaying the boot process. + +It is a defect of the software in question if it doesn't work this way, and +still pulls systemd-udev-settle.service into the boot process. + +Please file a bug report against the following units, with a request for it to +be updated to operate in a hotplug fashion without depending on +systemd-udev-settle.service: + + @OFFENDING_UNITS@ diff --git a/catalog/systemd.pl.catalog.in b/catalog/systemd.pl.catalog.in index 1a6c2546e..f82bab8fc 100644 --- a/catalog/systemd.pl.catalog.in +++ b/catalog/systemd.pl.catalog.in @@ -298,7 +298,8 @@ Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona. Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje Defined-By: systemd Support: %SUPPORT_URL% -Documentation: man:systemd-resolved.service(8) resolved.conf(5) +Documentation: man:systemd-resolved.service(8) +Documentation: man:resolved.conf(5) Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona. @@ -411,3 +412,115 @@ i jądro wymusiło zakończenie jego działania. Proszę zauważyć, że brak pamięci mógł nie zostać spowodowany przez jednostkę @UNIT@. + +-- b61fdac612e94b9182285b998843061f +Subject: Przyjmowanie nazwy użytkownika/grupy @USER_GROUP_NAME@, która nie zgadza się ze ścisłymi regułami nazw użytkowników/grup. +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: https://systemd.io/USER_NAMES + +Podano nazwę użytkownika/grupy @USER_GROUP_NAME@, co zostało przyjęte +zgodnie z rozluźnionymi regułami nazw użytkowników/grup, ale nie spełnia +ścisłych reguł. + +Ścisłe reguły nazw użytkowników/grup zapisane jako wyrażenie regularne to: + +^[a-zA-Z_][a-zA-Z0-9_-]{0,30}$ + +Rozluźnione reguły nazw użytkowników/grup przyjmują wszystkie nazwy, +oprócz pustego ciągu, nazw zawierających bajty NUL, znaki kontrolne, +dwukropki lub ukośniki, nazw niebędących prawidłowym tekstem UTF-8, +nazw z początkową lub końcową spacją, ciągów „.” lub „..”, ciągów +zawierających tylko cyfry lub ciągów zaczynających się myślnikiem +i zawierających oprócz niego tylko cyfry. + +-- 1b3bb94037f04bbf81028e135a12d293 +Subject: Utworzenie prawidłowej nazwy jednostki ze ścieżki „@MOUNT_POINT@” się nie powiodło. +Defined-By: systemd +Support: %SUPPORT_URL% + +Następująca ścieżka do punktu montowania nie może zostać przekonwertowana +na prawidłową nazwę jednostki .mount: + + @MOUNT_POINT@ + +Zwykle oznacza to, że ścieżka do punktu montowania jest dłuższa niż dozwolona +dla prawidłowych nazw jednostek. + +systemd dynamicznie syntetyzuje jednostki .mount dla wszystkich punktów +montowania pojawiających się na komputerze. Stosowany do tego jest prosty +algorytm: używana jest bezwzględna nazwa ścieżki ze wszystkimi znakami „/” +zastąpionymi znakami „-” (początkowy jest usuwany). Co więcej, wszystkie +niealfanumeryczne znaki (a także „:”, „-”, „_”, „.”, „\”) są zastępowane +„\xNN”, gdzie „NN” jest szesnastkowym kodem znaki. Na końcu dołączany jest +przyrostek „.mount”. Wynikowy ciąg musi mieć poniżej 256 znaków długości, +aby był prawidłową nazwą jednostki. To ograniczenie obowiązuje, aby wszystkie +nazwy jednostek mogły być używane także jako nazwy plików. Jeśli punkt +montowania — po zastosowaniu algorytmu — jest dłuższy niż to ograniczenie, +to nie może zostać zmapowany do jednostki. W takim przypadku systemd nie +zsyntetyzuje jednostki i nie może być używany do zarządzania punktem +montowania. Nie pojawi się w tabeli jednostek menedżera usług, przez co +nie zostanie także bezpiecznie i automatycznie zdjęty podczas wyłączania +komputera. + +Zasadniczo zalecane jest unikanie takich za długich ścieżek do punktów +montowania, a jeśli są używane mimo to, zarządzanie nimi niezależnie +od systemd, tzn. stawianie ich i automatyczne zdejmowanie podczas +wyłączania komputera przez inne oprogramowanie. + +-- b480325f9c394a7b802c231e51a2752c +Subject: Skonfigurowany jest szczególny użytkownik @OFFENDING_USER@, jest to niebezpieczne! +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: https://systemd.io/UIDS-GIDS + +Jednostka @UNIT@ jest skonfigurowana do używania User=@OFFENDING_USER@. + +Nie jest to bezpieczne. Głównym zastosowaniem użytkownika @OFFENDING_USER@ +w linuksowych systemach operacyjnych jest bycie właścicielem plików, których +nie można w inny sposób zmapować do żadnego lokalnego użytkownika. Jest +używany między innymi przez klienta NFS i przestrzenie nazw użytkowników +Linuksa. Wykonywanie procesów jednostki pod tożsamością tego użytkownika +może spowodować, że będą one miały dostęp do odczytu, a może nawet do zapisu, +plików, których nie można zmapować w inny sposób. + +Mocno zalecane jest unikanie wykonywania usług pod tą tożsamością użytkownika, +zwłaszcza na komputerach używających NFS lub mających kontenery. Należy +przydzielić identyfikator użytkownika dla tej konkretnej usługi, statycznie +przez systemd-sysusers lub dynamicznie przez ustawienie usługi DynamicUser=. + +-- 1c0454c1bd2241e0ac6fefb4bc631433 +Subject: Usługa systemd-udev-settle.service jest przestarzała. +Defined-By: systemd +Support: %SUPPORT_URL% + +Użycie jednostki usługi systemd „systemd-udev-settle.service” jest +przestarzałe. Wstawia ona sztuczne opóźnienie do procesu uruchamiania +bez dostarczania gwarancji, które były oczekiwane przez pozostałe +podsystemy. Korzystanie z tej usługi może prowadzić do hazardów, +i zasadniczo jest błędem. + +W przeszłości zadaniem tej usługi było oczekiwanie, aż wszystkie urządzenia +komputera zostaną w pełni wykryte i zainicjowane, opóźniając uruchamianie +do ukończenia tego etapu. Jednakże, współczesne komputery i urządzenia +na ogół nie działają już w ten sposób, tylko mogą pojawić się w dowolnej +chwili i zająć dowolny czas na wykrycie i inicjację. Z tego powodu, +w ogólnym przypadku, nie jest już możliwe poprawne opóźnienie uruchamiania +do przetworzenia „wszystkich urządzeń”, ponieważ nie jest jasne, co znaczy +„wszystkie urządzenia” i kiedy zostały odnalezione. Dotyczy to zwłaszcza +urządzeń podłączonych przez USB lub sieć. + +Nowoczesne oprogramowanie wymagające określonego sprzętu (takiego jak +urządzenie sieciowe lub urządzenie blokowe) do działania powinno oczekiwać +tylko na pojawienie się danego urządzenia, a w przeciwnym razie działać +asynchronicznie, inicjując urządzenia, kiedy te pojawiają się w trakcie +uruchamiania i w trakcie działania systemu bez opóźniania procesu uruchamiania. + +Jest to wada danego oprogramowania, jeśli nie działa ono w ten sposób +i nadal wciąga usługę systemd-udev-settle.service do procesu uruchamiania. + +Prosimy zgłosić błąd w następujących jednostkach z prośbą +o ich aktualizację tak, aby działały w sposób dynamiczny +bez zależności od usługi systemd-udev-settle.service: + + @OFFENDING_UNITS@ diff --git a/coccinelle/set_ensure_put.cocci b/coccinelle/set_ensure_put.cocci new file mode 100644 index 000000000..92d7970ad --- /dev/null +++ b/coccinelle/set_ensure_put.cocci @@ -0,0 +1,18 @@ +@@ +local idexpression r; +expression p, k, x; +@@ +- r = set_ensure_allocated(&p, k); +- if (r < 0) +- return ...; +- r = set_put(p, x); ++ r = set_ensure_put(&p, k, x); +@@ +local idexpression r; +expression p, k, x; +@@ +- r = set_ensure_allocated(p, k); +- if (r < 0) +- return ...; +- r = set_put(*p, x); ++ r = set_ensure_put(p, k, x); diff --git a/coccinelle/strempty.cocci b/coccinelle/strempty.cocci index 7901da365..0868184c5 100644 --- a/coccinelle/strempty.cocci +++ b/coccinelle/strempty.cocci @@ -1,6 +1,18 @@ @@ -/* Avoid running this transformation on the strempty function itself */ -position p : script:python() { p[0].current_element != "strempty" }; +/* Avoid running this transformation on the strempty function itself and + * on the "make_expression" macro in src/libsystemd/sd-bus/bus-convenience.c. + * As Coccinelle's Location object doesn't support macro "detection", use + * a pretty horrifying combo of specifying a file and a special "something_else" + * position element, which is, apparently, the default value of + * "current_element" before it's set (according to the source code), thus + * matching any "top level" position, including macros. Let's hope we never + * introduce a function called "something_else"... + */ +position p : script:python() { + not (p[0].current_element == "strempty" or + (p[0].file == "src/libsystemd/sd-bus/bus-convenience.c" and + p[0].current_element == "something_else")) +}; expression s; @@ ( diff --git a/docs/AUTOMATIC_BOOT_ASSESSMENT.md b/docs/AUTOMATIC_BOOT_ASSESSMENT.md index aff203b59..83ddf28fd 100644 --- a/docs/AUTOMATIC_BOOT_ASSESSMENT.md +++ b/docs/AUTOMATIC_BOOT_ASSESSMENT.md @@ -101,9 +101,9 @@ Here's an example walkthrough of how this all fits together. 6. If this boot also fails, on the next boot the boot loader will see the tag `+0-3`, i.e. the counter reached zero. At this point the entry will be - considered "bad", and ordered to the end of the list of entries. The next - newest boot entry is now tried, i.e. the system automatically reverted back - to an earlier version. + considered "bad", and ordered to the beginning of the list of entries. The + next newest boot entry is now tried, i.e. the system automatically reverted + back to an earlier version. The above describes the walkthrough when the selected boot entry continuously fails. Let's have a look at an alternative ending to this walkthrough. In this diff --git a/docs/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md index 514b8cd11..803ba5440 100644 --- a/docs/BOOT_LOADER_SPECIFICATION.md +++ b/docs/BOOT_LOADER_SPECIFICATION.md @@ -47,7 +47,7 @@ functionality. Here's why we think that it is not enough for our uses: * The various EFI implementations implement the boot order/boot item logic to different levels. Some firmware implementations do not offer a boot menu at all and instead unconditionally follow the EFI boot order, booting the first item that is working. * If the firmware setup is used to reset all data usually all EFI boot entries are lost, making the system entirely unbootable, as the firmware setups generally do not offer a UI to define additional boot items. By placing the menu item information on disk, it is always available, regardless if the BIOS setup data is lost. -* Harddisk images should be moveable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone. +* Harddisk images should be movable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone. * EFI is not universal yet (especially on non-x86 platforms), this specification is useful both for EFI and non-EFI boot loaders. * Many EFI systems disable USB support during early boot to optimize boot times, thus making keyboard input unavailable in the EFI menu. It is thus useful if the OS UI has a standardized way to discover available boot options which can be booted to. @@ -95,7 +95,7 @@ Note that the `$BOOT` partition is not supposed to be exclusive territory of this specification. This specification only defines semantics of the `/loader/` directory inside the file system (see below), but it doesn't intend to define ownership of the whole file system exclusively. Boot loaders, firmware, and -other software implementating this specification may choose to place other +other software implementing this specification may choose to place other files and directories in the same file system. For example, boot loaders that implement this specification might install their own boot code into the `$BOOT` partition. On systems where `$BOOT` is the ESP this is a particularly common diff --git a/docs/CGROUP_DELEGATION.md b/docs/CGROUP_DELEGATION.md index d05503bc9..4011f0932 100644 --- a/docs/CGROUP_DELEGATION.md +++ b/docs/CGROUP_DELEGATION.md @@ -131,6 +131,8 @@ If you wonder how to detect which of these three modes is currently used, use you are either in legacy or hybrid mode. To distinguish these two cases, run `statfs()` again on `/sys/fs/cgroup/unified/`. If that succeeds and reports `CGROUP2_SUPER_MAGIC` you are in hybrid mode, otherwise not. +From a shell, you can use check the `Type` in `stat -f /sys/fs/cgroup` and +`stat -f /sys/fs/cgroup/unified`. ## systemd's Unit Types diff --git a/docs/CODE_QUALITY.md b/docs/CODE_QUALITY.md index 9f261474a..a724d663f 100644 --- a/docs/CODE_QUALITY.md +++ b/docs/CODE_QUALITY.md @@ -71,5 +71,8 @@ available functionality: See [Testing systemd using sanitizers](https://systemd.io/TESTING_WITH_SANITIZERS) 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). + 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 c8ca8bfef..f335a1012 100644 --- a/docs/CODING_STYLE.md +++ b/docs/CODING_STYLE.md @@ -424,7 +424,7 @@ layout: default ## Deadlocks -- Do not issue NSS requests (that includes user name and host name lookups) +- Do not issue NSS requests (that includes user name and hostname lookups) from PID 1 as this might trigger deadlocks when those lookups involve synchronously talking to services that we would need to start up. @@ -521,7 +521,7 @@ layout: default hence we might want to call it "big endian" right-away. - Please never use `dup()`. Use `fcntl(fd, F_DUPFD_CLOEXEC, 3)` instead. For - two reason: first, you want `O_CLOEXEC` set on the new `fd` (see + two reasons: first, you want `O_CLOEXEC` set on the new `fd` (see above). Second, `dup()` will happily duplicate your `fd` as 0, 1, 2, i.e. stdin, stdout, stderr, should those `fd`s be closed. Given the special semantics of those `fd`s, it's probably a good idea to avoid diff --git a/docs/CONTAINER_INTERFACE.md b/docs/CONTAINER_INTERFACE.md index 71f9185c5..a36d2edc7 100644 --- a/docs/CONTAINER_INTERFACE.md +++ b/docs/CONTAINER_INTERFACE.md @@ -121,6 +121,16 @@ manager, please consider supporting the following interfaces. `container_ttys=pts/7 pts/8 pts/14` it will spawn three additional login gettys on ptys 7, 8, and 14. +4. To allow applications to detect the OS version and other metadata of the host + running the container manager, if this is considered desirable, please parse + the host's `/etc/os-release` and set a `$container_host_=` + environment variable for the ID fields described by the [os-release + interface](https://www.freedesktop.org/software/systemd/man/os-release.html), eg: + `$container_host_id=debian` + `$container_host_build_id=2020-06-15` + `$container_host_variant_id=server` + `$container_host_version_id=10` + ## Advanced Integration 1. Consider syncing `/etc/localtime` from the host file system into the diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 0ee71b274..7d7bb38eb 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -11,7 +11,7 @@ We welcome contributions from everyone. However, please follow the following gui ## Filing Issues * We use [GitHub Issues](https://github.com/systemd/systemd/issues) **exclusively** for tracking **bugs** and **feature** **requests** of systemd. If you are looking for help, please contact [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) instead. -* We only track bugs in the **two** **most** **recently** **released** **versions** of systemd in the GitHub Issue tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead. +* We only track bugs in the **two** **most** **recently** **released** (non-rc) **versions** of systemd in the GitHub Issue tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead (see below). See [GitHub Release Page](https://github.com/systemd/systemd/releases) for the list of most recent releases. * When filing an issue, specify the **systemd** **version** you are experiencing the issue with. Also, indicate which **distribution** you are using. * Please include an explanation how to reproduce the issue you are pointing out. @@ -20,7 +20,7 @@ Following these guidelines makes it easier for us to process your issue, and ens ### Older downstream versions For older versions that are still supported by your distribution please use respective downstream tracker: * **Fedora** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=systemd) -* **RHEL-7/CentOS-7** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Red%20Hat%20Enterprise%20Linux%207&component=systemd) or [systemd-rhel github](https://github.com/lnykryn/systemd-rhel/issues) +* **RHEL/CentOS** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel github](https://github.com/systemd-rhel/) * **Debian** - [bugs.debian.org](https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=systemd) ## Security vulnerability reports diff --git a/docs/CONVERTING_TO_HOMED.md b/docs/CONVERTING_TO_HOMED.md new file mode 100644 index 000000000..78b6c6163 --- /dev/null +++ b/docs/CONVERTING_TO_HOMED.md @@ -0,0 +1,135 @@ +--- +title: Converting Existing Users to systemd-homed +category: Users, Groups and Home Directories +layout: default +--- + +# Converting Existing Users to systemd-homed managed Users + +Traditionally on most Linux distributions, regular (human) users are managed +via entries in `/etc/passwd`, `/etc/shadow`, `/etc/group` and +`/etc/gshadow`. With the advent of +[`systemd-homed`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html) +it might be desirable to convert an existing, traditional user account to a +`systemd-homed` managed one. Below is a brief guide how to do that. + +Before continuing, please read up on these basic concepts: + +* [Home Directories](https://systemd.io/HOME_DIRECTORY) +* [JSON User Records](https://systemd.io/USER_RECORD) +* [JSON Group Records](https://systemd.io/GROUP_RECORD) +* [User/Group Record Lookup API via Varlink](https://systemd.io/USER_GROUP_API) + +## Caveat + +This is a manual process, and possibly a bit fragile. Hence, do this at your +own risk, read up beforehand, and make a backup first. You know what's at +stake: your own home directory, i.e. all your personal data. + +## Step-By-Step + +Here's the step-by-step guide: + +0. Preparations: make sure you run a distribution that has `systemd-homed` + enabled and properly set up, including the necessary PAM and NSS + configuration updates. Make sure you have enough disk space in `/home/` for + a (temporary) second copy of your home directory. Make sure to backup your + home directory. Make sure to log out of your user account fully. Then log in + as root on the console. + +1. Rename your existing home directory to something safe. Let's say your user + ID is `foobar`. Then do: + + ``` + mv /home/foobar /home/foobar.saved + ``` + +2. Have a look at your existing user record, as stored in `/etc/passwd` and + related files. We want to use the same data for the new record, hence it's good + looking at the old data. Use commands such as: + + ``` + getent passwd foobar + getent shadow foobar + ``` + + This will tell you the `/etc/passwd` and `/etc/shadow` entries for your + user. For details about the fields, see the respective man pages + [passwd(5)](http://man7.org/linux/man-pages/man5/passwd.5.html) and + [shadow(5)](http://man7.org/linux/man-pages/man5/shadow.5.html). + + The fourth field in the `getent passwd foobar` output tells you the GID of + your user's main group. Depending on your distribution it's a group private + to the user, or a group shared by most local, regular users. Let's say the + GID reported is 1000, let's then query its details: + + ``` + getent group 1000 + ``` + + This will tell you the name of that group. If the name is the same as your + user name your distribution apparently provided you with a private group for + your user. If it doesn't match (and is something like `users`) it apparently + didn't. Note that `systemd-homed` will always manage a private group for + each user under the same name, hence if your distribution is one of the + latter kind, then there's a (minor) mismatch in structure when converting. + + Save the information reported by these three commands somewhere, for later + reference. + +3. Now edit your `/etc/passwd` file and remove your existing record + (i.e. delete a single line, the one of your user's account, leaving all + other lines unmodified). Similar for `/etc/shadow`, `/etc/group` (in case + you have a private group for your user) and `/etc/gshadow`. Most + distributions provide you with a tool for that, that adds safe + synchronization for these changes: `vipw`, `vipw -s`, `vigr` and `vigr -s`. + +4. At this point the old user account vanished, while the home directory still + exists safely under the `/home/foobar.saved` name. Let's now create a new + account with `systemd-homed`, using the same username and UID as before: + + ``` + homectl create foobar --uid=$UID --real-name=$GECOS + ``` + + In this command line, replace `$UID` by the UID you previously used, + i.e. the third field of the `getent passwd foobar` output above. Similar, + replace `$GECOS` by the GECOS field of your old account, i.e the fifth field + of the old output. If your distribution traditionally does not assign a + private group to regular user groups, then consider adding `--member-of=` + with the group name to get a modicum of compatibility with the status quo + ante: this way your new user account will still not have the old primary + group as new primary group, but will have it as auxiliary group. + + Consider reading through the + [homectl(1)](https://www.freedesktop.org/software/systemd/man/homectl.html) + manual page at this point, maybe there are a couple of other settings you + want to set for your new account. In particular, look at `--storage=` and + `--disk-size=`, in order to change how your home directory shall be stored + (the default `luks` storage is recommended). + +5. Your new user account exists now, but it has an empty home directory. Let's + now migrate your old home directory into it. For that let's mount the new + home directory temporarily and copy the data in. + + ``` + homectl with foobar -- rsync -aHAXv --remove-source-files /home/foobar.saved/ . + ``` + + This mounts the home directory of the user, and then runs the specified + `rsync` command which copies the contents of the old home directory into the + new. The new home directory is the working directory of the invoked `rsync` + process. We are invoking this command as root, hence the `rsync` runs as + root too. When the `rsync` command completes the home directory is + automatically unmounted again. Since we used `--remove-source-files` all files + copied are removed from the old home directory as the copy progresses. After + the command completes the old home directory should be empty. Let's remove + it hence: + + ``` + rmdir /home/foobar.saved + ``` + +And that's it, we are done already. You can log out now and should be able to +log in under your user account as usual, but now with `systemd-homed` managing +your home directory. diff --git a/docs/DESKTOP_ENVIRONMENTS.md b/docs/DESKTOP_ENVIRONMENTS.md new file mode 100644 index 000000000..9ae1aefb2 --- /dev/null +++ b/docs/DESKTOP_ENVIRONMENTS.md @@ -0,0 +1,117 @@ +--- +title: Desktop Environment Integration +category: Concepts +layout: default +--- + +# Desktop Environments + +NOTE: This document is a work-in-progress. + +## Single Graphical Session + +systemd only supports running one graphical session per user at a time. +While this might not have always been the case historically, having multiple +sessions for one user running at the same time is problematic. +The DBus session bus is shared between all the logins, and services that are +started must be implicitly assigned to the user's current graphical session. + +In principle it is possible to run a single graphical session across multiple +logind seats, and this could be a way to use more than one display per user. +When a user logs in to a second seat, the seat resources could be assigned +to the existing session, allowing the graphical environment to present it +is a single seat. +Currently nothing like this is supported or even planned. + +## Pre-defined systemd units + +[`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html) +defines the `graphical-session.target` and `graphical-session-pre.target` to +allow cross-desktop integration. Furthermore, systemd defines the three base +slices `background`, `app` and `session`. +All units should be placed into one of these slices depending on their purposes: + + * `session.slice`: Contains only processes essential to run the user's graphical session + * `app.slice`: Contains all normal applications that the user is running + * `background.slice`: Useful for low-priority background tasks + +The purpose of this grouping is to assign different priorities to the +applications. +This could e.g. mean reserving memory to session processes, +preferentially killing background tasks in out-of-memory situations +or assigning different memory/CPU/IO priorities to ensure that the session +runs smoothly under load. + +TODO: Will there be a default to place units into e.g. `app.slice` by default +rather than the root slice? + +## XDG standardization for applications + +To ensure cross-desktop compatibility and encourage sharing of good practices, +desktop environments should adhere to the following conventions: + + * Application units should follow the scheme `app[-]-[@].service` + or `app[-]--.scope` + e.g: + - `app-gnome-org.gnome.Evince@12345.service` + - `app-flatpak-org.telegram.desktop@12345.service` + - `app-KDE-org.kde.okular@12345.service` + - `app-org.kde.amarok.service` + - `app-org.gnome.Evince-12345.scope` + * Using `.service` units instead of `.scope` units, i.e. allowing systemd to + start the process on behalf of the caller, + instead of the caller starting the process and letting systemd know about it, + is encouraged. + * The RANDOM should be a string of random characters to ensure that multiple instances + of the application can be launched. + It can be omitted in the case of a non-transient application services which can ensure + multiple instances are not spawned, such as a DBus activated application. + * If no application ID is available, the launcher should generate a reasonable + name when possible (e.g. using `basename(argv[0])`). This name must not + contain a `-` character. + +This has the following advantages: + * Using the `app--` prefix means that the unit defaults can be + adjusted using desktop environment specific drop-in files. + * The application ID can be retrieved by stripping the prefix and postfix. + This in turn should map to the corresponding `.desktop` file when available + +TODO: Define the name of slices that should be used. +This could be `app---.slice`. + +TODO: Does it really make sense to insert the ``? In GNOME I am +currently using a drop-in to configure `BindTo=graphical-session.target`, +`CollectMode=inactive-or-failed` and `TimeoutSec=5s`. I feel that such a +policy makes sense, but it may make much more sense to just define a +global default for all (graphical) applications. + + * Should application lifetime be bound to the session? + * May the user have applications that do not belong to the graphical session (e.g. launched from SSH)? + * Could we maybe add a default `app-.service.d` drop-in configuration? + +## XDG autostart integration + +To allow XDG autostart integration, systemd ships a cross-desktop generator +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. + +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. + +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 +systemd service file for startup. + +## Startup and shutdown best practices + +Question here are: + + * Are there strong opinions on how the session-leader process should watch the user's session units? + * Should systemd/logind/… provide an integrated way to define a session in terms of a running *user* unit? + * Is having `gnome-session-shutdown.target` that is run with `replace-irreversibly` considered a good practice? diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index fec40b1e9..d6f5126ac 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -145,7 +145,7 @@ systemd-udevd: boot loader menu through EFI a file `/run/systemd/reboot-to-boot-loader-menu` is created whenever this is requested. The file contains the requested boot loader menu timeout in µs, formatted in ASCII decimals, or zero in case no - time-out is requested. This file may be checked for by services run during + timeout is requested. This file may be checked for by services run during system shutdown in order to request the appropriate operation from the boot loader in an alternative fashion. diff --git a/docs/GROUP_RECORD.md b/docs/GROUP_RECORD.md index 8f98b49c2..2ea0b73a3 100644 --- a/docs/GROUP_RECORD.md +++ b/docs/GROUP_RECORD.md @@ -1,6 +1,6 @@ --- title: JSON Group Records -category: Interfaces +category: Users, Groups and Home Directories layout: default --- diff --git a/docs/HACKING.md b/docs/HACKING.md index c0516b5c6..990f78c9e 100644 --- a/docs/HACKING.md +++ b/docs/HACKING.md @@ -36,9 +36,12 @@ building clean OS images from an upstream distribution in combination with a fresh build of the project in the local working directory. To make use of this, please acquire `mkosi` from https://github.com/systemd/mkosi first, unless your distribution has packaged it already and you can get it from there. After the -tool is installed it is sufficient to type `mkosi` in the systemd project -directory to generate a disk image `image.raw` you can boot either in -`systemd-nspawn` or in an UEFI-capable VM: +tool is installed, symlink the settings file for your distribution of choice from +.mkosi/ to mkosi.default in the project root directory (note that the package +manager for this distro needs to be installed on your host system). After doing +that, it is sufficient to type `mkosi` in the systemd project directory to +generate a disk image `image.raw` you can boot either in `systemd-nspawn` or in +an UEFI-capable VM: ``` # systemd-nspawn -bi image.raw @@ -72,22 +75,23 @@ Putting this all together, here's a series of commands for preparing a patch for systemd (this example is for Fedora): ```sh -$ sudo dnf builddep systemd # install build dependencies -$ sudo dnf install mkosi # install tool to quickly build images +$ sudo dnf builddep systemd # install build dependencies +$ sudo dnf install mkosi # install tool to quickly build images $ git clone https://github.com/systemd/systemd.git $ cd systemd -$ vim src/core/main.c # or wherever you'd like to make your changes -$ meson build # configure the build -$ ninja -C build # build it locally, see if everything compiles fine -$ ninja -C build test # run some simple regression tests -$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi -$ sudo mkosi # build a test image -$ sudo systemd-nspawn -bi image.raw # boot up the test image -$ git add -p # interactively put together your patch -$ git commit # commit it +$ vim src/core/main.c # or wherever you'd like to make your changes +$ meson build # configure the build +$ ninja -C build # build it locally, see if everything compiles fine +$ ninja -C build test # run some simple regression tests +$ ln -s .mkosi/mkosi.fedora mkosi.default # Configure mkosi to build a fedora image +$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi +$ sudo mkosi # build a test image +$ sudo systemd-nspawn -bi image.raw # boot up the test image +$ git add -p # interactively put together your patch +$ git commit # commit it $ git push REMOTE HEAD:refs/heads/BRANCH - # where REMOTE is your "fork" on GitHub - # and BRANCH is a branch name. + # where REMOTE is your "fork" on GitHub + # and BRANCH is a branch name. ``` And after that, head over to your repo on GitHub and click "Compare & pull request" @@ -98,7 +102,7 @@ Happy hacking! ## Fuzzers systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically -run by [OSS-Fuzz](https://github.com/google/oss-fuzz) and [Fuzzit](https://fuzzit.dev) with sanitizers. +run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers. To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput` function and add it to the list in `src/fuzz/meson.build`. @@ -118,10 +122,6 @@ python infra/helper.py build_fuzzers --sanitizer memory systemd ../systemd python infra/helper.py run_fuzzer systemd fuzz-foo ``` -When you add a new target you should also add the target on [Fuzzit](https://app.fuzzit.dev/admin/RxqRpGNXquIvqrmp4iJS/dashboard) - (Please ask someone with permissions). One the target is configured on Fuzzit you need to add it to - `travis-ci/managers/fuzzit.sh` so the new target will run sanity tests on every pull-request and periodic fuzzing jobs. - If you find a bug that impacts the security of systemd, please follow the guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulnerability. diff --git a/docs/HOME_DIRECTORY.md b/docs/HOME_DIRECTORY.md index 73c2359ff..a3eabb7e6 100644 --- a/docs/HOME_DIRECTORY.md +++ b/docs/HOME_DIRECTORY.md @@ -1,6 +1,6 @@ --- title: Home Directories -category: Concepts +category: Users, Groups and Home Directories layout: default --- @@ -125,7 +125,7 @@ medium. (Moreover it allows to embed additional partitions later on, for example for allowing 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 the LUKS2 header: +Rationale for including the encrypted user record in the LUKS2 header: Linux kernel file system implementations are generally not robust towards maliciously formatted file systems; there's a good chance that file system images can be used as attack vectors, exploiting the kernel. Thus it is @@ -147,7 +147,7 @@ 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 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 distuingish +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 @@ -168,6 +168,10 @@ 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 `discard` 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. + +When deactivating a home directory, the file system or block device is trimmed +or extended as configured in the `luksOfflineDiscard` setting of the user +record. diff --git a/docs/INITRD_INTERFACE.md b/docs/INITRD_INTERFACE.md index 8985f2761..e59bbcce1 100644 --- a/docs/INITRD_INTERFACE.md +++ b/docs/INITRD_INTERFACE.md @@ -36,7 +36,7 @@ interfaces are currently used by dracut and the ArchLinux initrds. optionally followed (in `argv[2]`, `argv[3]`, … systemd's original command line options, for example `--log-level=` and similar. -* Storage daemons run from the initrd should follow the the guide on [systemd +* Storage daemons run from the initrd should follow the guide on [systemd and Storage Daemons for the Root File System](https://systemd.io/ROOT_STORAGE_DAEMONS) to survive properly from the boot initrd all the way to the point where systemd jumps back into the initrd diff --git a/docs/JOURNAL_FILE_FORMAT.md b/docs/JOURNAL_FILE_FORMAT.md new file mode 100644 index 000000000..523ed621f --- /dev/null +++ b/docs/JOURNAL_FILE_FORMAT.md @@ -0,0 +1,694 @@ +--- +title: Journal File Format +category: Interfaces +layout: default +--- + +# Journal File Format + +_Note that this document describes the binary on-disk format of journals +only. For interfacing with web technologies there's the [Journal JSON +Format](http://www.freedesktop.org/wiki/Software/systemd/json). For transfer +of journal data across the network there's the [Journal Export +Format](http://www.freedesktop.org/wiki/Software/systemd/export)._ + +The systemd journal stores log data in a binary format with several features: + +* Fully indexed by all fields +* Can store binary data, up to 2^64-1 in size +* Seekable +* Primarily append-based, hence robust to corruption +* Support for in-line compression +* Support for in-line Forward Secure Sealing + +This document explains the basic structure of the file format on disk. We are +making this available primarily to allow review and provide documentation. Note +that the actual implementation in the [systemd +codebase](https://github.com/systemd/systemd/blob/master/src/journal/) is the +only ultimately authoritative description of the format, so if this document +and the code disagree, the code is right. That said we'll of course try hard to +keep this document up-to-date and accurate. + +Instead of implementing your own reader or writer for journal files we ask you +to use the [Journal's native C +API](http://www.freedesktop.org/software/systemd/man/sd-journal.html) to access +these files. It provides you with full access to the files, and will not +withhold any data. If you find a limitation, please ping us and we might add +some additional interfaces for you. + +If you need access to the raw journal data in serialized stream form without C +API our recommendation is to make use of the [Journal Export +Format](http://www.freedesktop.org/wiki/Software/systemd/export), which you can +get via "journalctl -o export" or via systemd-journal-gatewayd. The export +format is much simpler to parse, but complete and accurate. Due to its +stream-based nature it is not indexed. + +_Or, to put this in other words: this low-level document is probably not what +you want to use as base of your project. You want our [C +API](http://www.freedesktop.org/software/systemd/man/sd-journal.html) instead! +And if you really don't want the C API, then you want the [Journal Export +Format](http://www.freedesktop.org/wiki/Software/systemd/export) instead! This +document is primarily for your entertainment and education. Thank you!_ + +This document assumes you have a basic understanding of the journal concepts, +the properties of a journal entry and so on. If not, please go and read up, +then come back! This is a good opportunity to read about the [basic properties +of journal +entries](http://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. + +This document describes the current format of systemd 246. The documented +format is compatible with the format used in the first versions of the journal, +but received various compatible and incompatible additions since. + +If you are wondering why the journal file format has been created in the first +place instead of adopting an existing database implementation, please have a +look [at this +thread](https://lists.freedesktop.org/archives/systemd-devel/2012-October/007054.html). + + +## Basics + +* All offsets, sizes, time values, hashes (and most other numeric values) are 64bit unsigned integers in LE format. +* Offsets are always relative to the beginning of the file. +* The 64bit hash function siphash24 is used for newer journal files. For older files [Jenkins lookup3](https://en.wikipedia.org/wiki/Jenkins_hash_function) is used, more specifically `jenkins_hashlittle2()` with the first 32bit integer it returns as higher 32bit part of the 64bit value, and the second one uses as lower 32bit part. +* All structures are aligned to 64bit boundaries and padded to multiples of 64bit +* The format is designed to be read and written via memory mapping using multiple mapped windows. +* All time values are stored in usec since the respective epoch. +* Wall clock time values are relative to the Unix time epoch, i.e. January 1st, 1970. (`CLOCK_REALTIME`) +* Monotonic time values are always stored jointly with the kernel boot ID value (i.e. `/proc/sys/kernel/random/boot_id`) they belong to. They tend to be relative to the start of the boot, but aren't for containers. (`CLOCK_MONOTONIC`) +* Randomized, unique 128bit IDs are used in various locations. These are generally UUID v4 compatible, but this is not a requirement. + +## General Rules + +If any kind of corruption is noticed by a writer it should immediately rotate +the file and start a new one. No further writes should be attempted to the +original file, but it should be left around so that as little data as possible +is lost. + +If any kind of corruption is noticed by a reader it should try hard to handle +this gracefully, such as skipping over the corrupted data, but allowing access +to as much data around it as possible. + +A reader should verify all offsets and other data as it reads it. This includes +checking for alignment and range of offsets in the file, especially before +trying to read it via a memory map. + +A reader must interleave rotated and corrupted files as good as possible and +present them as single stream to the user. + +All fields marked as "reserved" must be initialized with 0 when writing and be +ignored on reading. They are currently not used but might be used later on. + + +## Structure + +The file format's data structures are declared in +[journal-def.h](https://github.com/systemd/systemd/blob/master/src/journal/journal-def.h). + +The file format begins with a header structure. After the header structure +object structures follow. Objects are appended to the end as time +progresses. Most data stored in these objects is not altered anymore after +having been written once, with the exception of records necessary for +indexing. When new data is appended to a file the writer first writes all new +objects to the end of the file, and then links them up at front after that's +done. Currently, seven different object types are known: + +```c +enum { + OBJECT_UNUSED, + OBJECT_DATA, + OBJECT_FIELD, + OBJECT_ENTRY, + OBJECT_DATA_HASH_TABLE, + OBJECT_FIELD_HASH_TABLE, + OBJECT_ENTRY_ARRAY, + OBJECT_TAG, + _OBJECT_TYPE_MAX +}; +``` + +* A **DATA** object, which encapsulates the contents of one field of an entry, i.e. a string such as `_SYSTEMD_UNIT=avahi-daemon.service`, or `MESSAGE=Foobar made a booboo.` but possibly including large or binary data, and always prefixed by the field name and "=". +* A **FIELD** object, which encapsulates a field name, i.e. a string such as `_SYSTEMD_UNIT` or `MESSAGE`, without any `=` or even value. +* An **ENTRY** object, which binds several **DATA** objects together into a log entry. +* A **DATA_HASH_TABLE** object, which encapsulates a hash table for finding existing **DATA** objects. +* A **FIELD_HASH_TABLE** object, which encapsulates a hash table for finding existing **FIELD** objects. +* An **ENTRY_ARRAY** object, which encapsulates a sorted array of offsets to entries, used for seeking by binary search. +* A **TAG** object, consisting of an FSS sealing tag for all data from the beginning of the file or the last tag written (whichever is later). + +## Header + +The Header struct defines, well, you guessed it, the file header: + +```c +_packed_ struct Header { + uint8_t signature[8]; /* "LPKSHHRH" */ + le32_t compatible_flags; + le32_t incompatible_flags; + uint8_t state; + uint8_t reserved[7]; + sd_id128_t file_id; + sd_id128_t machine_id; + sd_id128_t boot_id; /* last writer */ + sd_id128_t seqnum_id; + le64_t header_size; + le64_t arena_size; + le64_t data_hash_table_offset; + le64_t data_hash_table_size; + le64_t field_hash_table_offset; + le64_t field_hash_table_size; + le64_t tail_object_offset; + le64_t n_objects; + le64_t n_entries; + le64_t tail_entry_seqnum; + le64_t head_entry_seqnum; + le64_t entry_array_offset; + le64_t head_entry_realtime; + le64_t tail_entry_realtime; + le64_t tail_entry_monotonic; + /* Added in 187 */ + le64_t n_data; + le64_t n_fields; + /* Added in 189 */ + le64_t n_tags; + le64_t n_entry_arrays; + /* Added in 246 */ + le64_t data_hash_chain_depth; + le64_t field_hash_chain_depth; +}; +``` + +The first 8 bytes of Journal files must contain the ASCII characters `LPKSHHRH`. + +If a writer finds that the **machine_id** of a file to write to does not match +the machine it is running on it should immediately rotate the file and start a +new one. + +When journal file is first created the **file_id** is randomly and uniquely +initialized. + +When a writer opens a file it shall initialize the **boot_id** to the current +boot id of the system. + +The currently used part of the file is the **header_size** plus the +**arena_size** field of the header. If a writer needs to write to a file where +the actual file size on disk is smaller than the reported value it shall +immediately rotate the file and start a new one. If a writer is asked to write +to a file with a header that is shorter than his own definition of the struct +Header, he shall immediately rotate the file and start a new one. + +The **n_objects** field contains a counter for objects currently available in +this file. As objects are appended to the end of the file this counter is +increased. + +The first object in the file starts immediately after the header. The last +object in the file is at the offset **tail_object_offset**, which may be 0 if +no object is in the file yet. + +The **n_entries**, **n_data**, **n_fields**, **n_tags**, **n_entry_arrays** are +counters of the objects of the specific types. + +**tail_entry_seqnum** and **head_entry_seqnum** contain the sequential number +(see below) of the last or first entry in the file, respectively, or 0 if no +entry has been written yet. + +**tail_entry_realtime** and **head_entry_realtime** contain the wallclock +timestamp of the last or first entry in the file, respectively, or 0 if no +entry has been written yet. + +**tail_entry_monotonic** is the monotonic timestamp of the last entry in the +file, referring to monotonic time of the boot identified by **boot_id**. + +**data_hash_chain_depth** is a counter of the deepest chain in the data hash +table, minus one. This is updated whenever a chain is found that is longer than +the previous deepest chain found. Note that the counter is updated during hash +table lookups, as the chains are traversed. This counter is used to determine +when it is a good time to rotate the journal file, because hash collisions +became too frequent. + +Similar, **field_hash_chain_depth** is a counter of the deepest chain in the +field hash table, minus one. + + +## Extensibility + +The format is supposed to be extensible in order to enable future additions of +features. Readers should simply skip objects of unknown types as they read +them. If a compatible feature extension is made a new bit is registered in the +header's **compatible_flags** field. If a feature extension is used that makes +the format incompatible a new bit is registered in the header's +**incompatible_flags** field. Readers should check these two bit fields, if +they find a flag they don't understand in compatible_flags they should continue +to read the file, but if they find one in **incompatible_flags** they should +fail, asking for an update of the software. Writers should refuse writing if +there's an unknown bit flag in either of these fields. + +The file header may be extended as new features are added. The size of the file +header is stored in the header. All header fields up to **n_data** are known to +unconditionally exist in all revisions of the file format, all fields starting +with **n_data** needs to be explicitly checked for via a size check, since they +were additions after the initial release. + +Currently only five extensions flagged in the flags fields are known: + +```c +enum { + HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, + HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, + HEADER_INCOMPATIBLE_KEYED_HASH = 1 << 2, + HEADER_INCOMPATIBLE_COMPRESSED_ZSTD = 1 << 3, +}; + +enum { + HEADER_COMPATIBLE_SEALED = 1 << 0, +}; +``` + +HEADER_INCOMPATIBLE_COMPRESSED_XZ indicates that the file includes DATA objects +that are compressed using XZ. Similarly, HEADER_INCOMPATIBLE_COMPRESSED_LZ4 +indicates that the file includes DATA objects that are compressed with the LZ4 +algorithm. And HEADER_INCOMPATIBLE_COMPRESSED_ZSTD indicates that there are +objects compressed with ZSTD. + +HEADER_INCOMPATIBLE_KEYED_HASH indicates that instead of the unkeyed Jenkins +hash function the keyed siphash24 hash function is used for the two hash +tables, see below. + +HEADER_COMPATIBLE_SEALED indicates that the file includes TAG objects required +for Forward Secure Sealing. + + +## Dirty Detection + +```c +enum { + STATE_OFFLINE = 0, + STATE_ONLINE = 1, + STATE_ARCHIVED = 2, + _STATE_MAX +}; +``` + +If a file is opened for writing the **state** field should be set to +STATE_ONLINE. If a file is closed after writing the **state** field should be +set to STATE_OFFLINE. After a file has been rotated it should be set to +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 +the file to ensure the dirty state hits disk. + + +## Sequence Numbers + +All entries carry sequence numbers that are monotonically counted up for each +entry (starting at 1) and are unique among all files which carry the same +**seqnum_id** field. This field is randomly generated when the journal daemon +creates its first file. All files generated by the same journal daemon instance +should hence carry the same seqnum_id. This should guarantee a monotonic stream +of sequential numbers for easy interleaving even if entries are distributed +among several files, such as the system journal and many per-user journals. + + +## Concurrency + +The file format is designed to be usable in a simultaneous +single-writer/multiple-reader scenario. The synchronization model is very weak +in order to facilitate storage on the most basic of file systems (well, the +most basic ones that provide us with `mmap()` that is), and allow good +performance. No file locking is used. The only time where disk synchronization +via `fdatasync()` should be enforced is after and before changing the **state** +field in the file header (see below). It is recommended to execute a memory +barrier after appending and initializing new objects at the end of the file, +and before linking them up in the earlier objects. + +This weak synchronization model means that it is crucial that readers verify +the structural integrity of the file as they read it and handle invalid +structure gracefully. (Checking what you read is a pretty good idea out of +security considerations anyway.) This specifically includes checking offset +values, and that they point to valid objects, with valid sizes and of the type +and hash value expected. All code must be written with the fact in mind that a +file with inconsistent structure might just be inconsistent temporarily, and +might become consistent later on. Payload OTOH requires less scrutiny, as it +should only be linked up (and hence visible to readers) after it was +successfully written to memory (though not necessarily to disk). On non-local +file systems it is a good idea to verify the payload hashes when reading, in +order to avoid annoyances with `mmap()` inconsistencies. + +Clients intending to show a live view of the journal should use `inotify()` for +this to watch for files changes. Since file writes done via `mmap()` do not +result in `inotify()` writers shall truncate the file to its current size after +writing one or more entries, which results in inotify events being +generated. Note that this is not used as a transaction scheme (it doesn't +protect anything), but merely for triggering wakeups. + +Note that inotify will not work on network file systems if reader and writer +reside on different hosts. Readers which detect they are run on journal files +on a non-local file system should hence not rely on inotify for live views but +fall back to simple time based polling of the files (maybe recheck every 2s). + + +## Objects + +All objects carry a common header: + +```c +enum { + OBJECT_COMPRESSED_XZ = 1 << 0, + OBJECT_COMPRESSED_LZ4 = 1 << 1, + OBJECT_COMPRESSED_ZSTD = 1 << 2, +}; + +_packed_ struct ObjectHeader { + uint8_t type; + uint8_t flags; + uint8_t reserved[6]; + le64_t size; + uint8_t payload[]; +}; +``` + +The **type** field is one of the object types listed above. The **flags** field +currently knows three flags: OBJECT_COMPRESSED_XZ, OBJECT_COMPRESSED_LZ4 and +OBJECT_COMPRESSED_ZSTD. It is only valid for DATA objects and indicates that +the data payload is compressed with XZ/LZ4/ZSTD. If one of the +OBJECT_COMPRESSED_* flags is set for an object then the matching +HEADER_INCOMPATIBLE_COMPRESSED_XZ/HEADER_INCOMPATIBLE_COMPRESSED_LZ4/HEADER_INCOMPATIBLE_COMPRESSED_ZSTD +flag must be set for the file as well. At most one of these three bits may be +set. The **size** field encodes the size of the object including all its +headers and payload. + + +## Data Objects + +```c +_packed_ struct DataObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t next_field_offset; + le64_t entry_offset; /* the first array entry we store inline */ + le64_t entry_array_offset; + le64_t n_entries; + uint8_t payload[]; +}; +``` + +Data objects carry actual field data in the **payload[]** array, including a +field name, a `=` and the field data. Example: +`_SYSTEMD_UNIT=foobar.service`. The **hash** field is a hash value of the +payload. If the `HEADER_INCOMPATIBLE_KEYED_HASH` flag is set in the file header +this is the siphash24 hash value of the payload, keyed by the file ID as stored +in the **file_id** field of the file header. If the flag is not set it is the +non-keyed Jenkins hash of the payload instead. The keyed hash is preferred as +it makes the format more robust against attackers that want to trigger hash +collisions in the hash table. + +**next_hash_offset** is used to link up DATA objects in the DATA_HASH_TABLE if +a hash collision happens (in a singly linked list, with an offset of 0 +indicating the end). **next_field_offset** is used to link up data objects with +the same field name from the FIELD object of the field used. + +**entry_offset** is an offset to the first ENTRY object referring to this DATA +object. **entry_array_offset** is an offset to an ENTRY_ARRAY object with +offsets to other entries referencing this DATA object. Storing the offset to +the first ENTRY object in-line is an optimization given that many DATA objects +will be referenced from a single entry only (for example, `MESSAGE=` frequently +includes a practically unique string). **n_entries** is a counter of the total +number of ENTRY objects that reference this object, i.e. the sum of all +ENTRY_ARRAYS chained up from this object, plus 1. + +The **payload[]** field contains the field name and date unencoded, unless +OBJECT_COMPRESSED_XZ/OBJECT_COMPRESSED_LZ4/OBJECT_COMPRESSED_ZSTD is set in the +`ObjectHeader`, in which case the payload is compressed with the indicated +compression algorithm. + + +## Field Objects + +```c +_packed_ struct FieldObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t head_data_offset; + uint8_t payload[]; +}; +``` + +Field objects are used to enumerate all possible values a certain field name +can take in the entire journal file. + +The **payload[]** array contains the actual field name, without '=' or any +field value. Example: `_SYSTEMD_UNIT`. The **hash** field is a hash value of +the payload. As for the DATA objects, this too is either the `.file_id` keyed +siphash24 hash of the payload, or the non-keyed Jenkins hash. + +**next_hash_offset** is used to link up FIELD objects in the FIELD_HASH_TABLE +if a hash collision happens (in singly linked list, offset 0 indicating the +end). **head_data_offset** points to the first DATA object that shares this +field name. It is the head of a singly linked list using DATA's +**next_field_offset** offset. + + +## Entry Objects + +``` +_packed_ struct EntryItem { + le64_t object_offset; + le64_t hash; +}; + +_packed_ struct EntryObject { + ObjectHeader object; + le64_t seqnum; + le64_t realtime; + le64_t monotonic; + sd_id128_t boot_id; + le64_t xor_hash; + EntryItem items[]; +}; +``` + +An ENTRY object binds several DATA objects together into one log entry, and +includes other metadata such as various timestamps. + +The **seqnum** field contains the sequence number of the entry, **realtime** +the realtime timestamp, and **monotonic** the monotonic timestamp for the boot +identified by **boot_id**. + +The **xor_hash** field contains a binary XOR of the hashes of the payload of +all DATA objects referenced by this ENTRY. This value is usable to check the +contents of the entry, being independent of the order of the DATA objects in +the array. Note that even for files that have the +`HEADER_INCOMPATIBLE_KEYED_HASH` flag set (and thus siphash24 the otherwise +used hash function) the hash function used for this field, as singular +exception, is the Jenkins lookup3 hash function. The XOR hash value is used to +quickly compare the contents of two entries, and to define a well-defined order +between two entries that otherwise have the same sequence numbers and +timestamps. + +The **items[]** array contains references to all DATA objects of this entry, +plus their respective hashes (which are calculated the same way as in the DATA +objects, i.e. keyed by the file ID). + +In the file ENTRY objects are written ordered monotonically by sequence +number. For continuous parts of the file written during the same boot +(i.e. with the same boot_id) the monotonic timestamp is monotonic too. Modulo +wallclock time jumps (due to incorrect clocks being corrected) the realtime +timestamps are monotonic too. + + +## Hash Table Objects + +```c +_packed_ struct HashItem { + le64_t head_hash_offset; + le64_t tail_hash_offset; +}; + +_packed_ struct HashTableObject { + ObjectHeader object; + HashItem items[]; +}; +``` + +The structure of both DATA_HASH_TABLE and FIELD_HASH_TABLE objects are +identical. They implement a simple hash table, which each cell containing +offsets to the head and tail of the singly linked list of the DATA and FIELD +objects, respectively. DATA's and FIELD's next_hash_offset field are used to +chain up the objects. Empty cells have both offsets set to 0. + +Each file contains exactly one DATA_HASH_TABLE and one FIELD_HASH_TABLE +objects. Their payload is directly referred to by the file header in the +**data_hash_table_offset**, **data_hash_table_size**, +**field_hash_table_offset**, **field_hash_table_size** fields. These offsets do +_not_ point to the object headers but directly to the payloads. When a new +journal file is created the two hash table objects need to be created right +away as first two objects in the stream. + +If the hash table fill level is increasing over a certain fill level (Learning +from Java's Hashtable for example: > 75%), the writer should rotate the file +and create a new one. + +The DATA_HASH_TABLE should be sized taking into account to the maximum size the +file is expected to grow, as configured by the administrator or disk space +considerations. The FIELD_HASH_TABLE should be sized to a fixed size; the +number of fields should be pretty static as it depends only on developers' +creativity rather than runtime parameters. + + +## Entry Array Objects + + +```c +_packed_ struct EntryArrayObject { + ObjectHeader object; + le64_t next_entry_array_offset; + le64_t items[]; +}; +``` + +Entry Arrays are used to store a sorted array of offsets to entries. Entry +arrays are strictly sorted by offsets on disk, and hence by their timestamps +and sequence numbers (with some restrictions, see above). + +Entry Arrays are chained up. If one entry array is full another one is +allocated and the **next_entry_array_offset** field of the old one pointed to +it. An Entry Array with **next_entry_array_offset** set to 0 is the last in the +list. To optimize allocation and seeking, as entry arrays are appended to a +chain of entry arrays they should increase in size (double). + +Due to being monotonically ordered entry arrays may be searched with a binary +search (bisection). + +One chain of entry arrays links up all entries written to the journal. The +first entry array is referenced in the **entry_array_offset** field of the +header. + +Each DATA object also references an entry array chain listing all entries +referencing a specific DATA object. Since many DATA objects are only referenced +by a single ENTRY the first offset of the list is stored inside the DATA object +itself, an ENTRY_ARRAY object is only needed if it is referenced by more than +one ENTRY. + + +## Tag Object + +```c +#define TAG_LENGTH (256/8) + +_packed_ struct TagObject { + ObjectHeader object; + le64_t seqnum; + le64_t epoch; + uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */ +}; +``` + +Tag objects are used to seal off the journal for alteration. In regular +intervals a tag object is appended to the file. The tag object consists of a +SHA-256 HMAC tag that is calculated from the objects stored in the file since +the last tag was written, or from the beginning if no tag was written yet. The +key for the HMAC is calculated via the externally maintained FSPRG logic for +the epoch that is written into **epoch**. The sequence number **seqnum** is +increased with each tag. When calculating the HMAC of objects header fields +that are volatile are excluded (skipped). More specifically all fields that +might validly be altered to maintain a consistent file structure (such as +offsets to objects added later for the purpose of linked lists and suchlike) +after an object has been written are not protected by the tag. This means a +verifier has to independently check these fields for consistency of +structure. For the fields excluded from the HMAC please consult the source code +directly. A verifier should read the file from the beginning to the end, always +calculating the HMAC for the objects it reads. Each time a tag object is +encountered the HMAC should be verified and restarted. The tag object sequence +numbers need to increase strictly monotonically. Tag objects themselves are +partially protected by the HMAC (i.e. seqnum and epoch is included, the tag +itself not). + + +## Algorithms + +### Reading + +Given an offset to an entry all data fields are easily found by following the +offsets in the data item array of the entry. + +Listing entries without filter is done by traversing the list of entry arrays +starting with the headers' **entry_array_offset** field. + +Seeking to an entry by timestamp or sequence number (without any matches) is +done via binary search in the entry arrays starting with the header's +**entry_array_offset** field. Since these arrays double in size as more are +added the time cost of seeking is O(log(n)*log(n)) if n is the number of +entries in the file. + +When seeking or listing with one field match applied the DATA object of the +match is first identified, and then its data entry array chain traversed. The +time cost is the same as for seeks/listings with no match. + +If multiple matches are applied, multiple chains of entry arrays should be +traversed in parallel. Since they all are strictly monotonically ordered by +offset of the entries, advancing in one can be directly applied to the others, +until an entry matching all matches is found. In the worst case seeking like +this is O(n) where n is the number of matching entries of the "loosest" match, +but in the common case should be much more efficient at least for the +well-known fields, where the set of possible field values tend to be closely +related. Checking whether an entry matches a number of matches is efficient +since the item array of the entry contains hashes of all data fields +referenced, and the number of data fields of an entry is generally small (< +30). + +When interleaving multiple journal files seeking tends to be a frequently used +operation, but in this case can be effectively suppressed by caching results +from previous entries. + +When listing all possible values a certain field can take it is sufficient to +look up the FIELD object and follow the chain of links to all DATA it includes. + +### Writing + +When an entry is appended to the journal for each of its data fields the data +hash table should be checked. If the data field does not yet exist in the file +it should be appended and added to the data hash table. When a field data +object is added the field hash table should be checked for the field name of +the data field, and a field object be added if necessary. After all data fields +(and recursively all field names) of the new entry are appended and linked up +in the hashtables the entry object should be appended and linked up too. + +In regular intervals a tag object should be written if sealing is enabled (see +above). Before the file is closed a tag should be written too, to seal it off. + +Before writing an object, time and disk space limits should be checked and +rotation triggered if necessary. + + +## Optimizing Disk IO + +_A few general ideas to keep in mind:_ + +The hash tables for looking up fields and data should be quickly in the memory +cache and not hurt performance. All entries and entry arrays are ordered +strictly by time on disk, and hence should expose an OK access pattern on +rotating media, when read sequentially (which should be the most common case, +given the nature of log data). + +The disk access patterns of the binary search for entries needed for seeking +are problematic on rotating disks. This should not be a major issue though, +since seeking should not be a frequent operation. + +When reading, collecting data fields for presenting entries to the user is +problematic on rotating disks. In order to optimize these patterns the item +array of entry objects should be sorted by disk offset before +writing. Effectively, frequently used data objects should be in the memory +cache quickly. Non-frequently used data objects are likely to be located +between the previous and current entry when reading and hence should expose an +OK access pattern. Problematic are data objects that are neither frequently nor +infrequently referenced, which will cost seek time. + +And that's all there is to it. + +Thanks for your interest! diff --git a/docs/PASSWORD_AGENTS.md b/docs/PASSWORD_AGENTS.md new file mode 100644 index 000000000..75b10da53 --- /dev/null +++ b/docs/PASSWORD_AGENTS.md @@ -0,0 +1,40 @@ +--- +title: Password Agents +category: Interfaces +layout: default +--- + +# Password Agents + +systemd 12 and newer support lightweight password agents which can be used to query the user for system-level passwords or passphrases. These are passphrases that are not related to a specific user, but to some kind of hardware or service. Right now this is used exclusively for encrypted hard-disk passphrases but later on this is likely to be used to query passphrases of SSL certificates at Apache startup time as well. The basic idea is that a system component requesting a password entry can simply drop a simple .ini-style file into `/run/systemd/ask-password` which multiple different agents may watch via `inotify()`, and query the user as necessary. The answer is then sent back to the querier via an `AF_UNIX`/`SOCK_DGRAM` socket. Multiple agents might be running at the same time in which case they all should query the user and the agent which answers first wins. Right now systemd ships with the following passphrase agents: + +* A Plymouth agent used for querying passwords during boot-up +* A console agent used in similar situations if Plymouth is not available +* A GNOME agent which can be run as part of the normal user session which pops up a notification message and icon which when clicked receives the passphrase from the user. This is useful and necessary in case an encrypted system hard-disk is plugged in when the machine is already up. +* A [`wall(1)`](http://man7.org/linux/man-pages/man1/wall.1.html) agent which sends wall messages as soon as a password shall be entered. +* A simple tty agent which is built into "`systemctl start`" (and similar commands) and asks passwords to the user during manual startup of a service +* A simple tty agent which can be run manually to respond to all queued passwords + +It is easy to write additional agents. The basic algorithm to follow looks like this: + +* Create an inotify watch on /run/systemd/ask-password, watch for `IN_CLOSE_WRITE|IN_MOVED_TO` +* Ignore all events on files in that directory that do not start with "`ask.`" +* As soon as a file named "`ask.xxxx`" shows up, read it. It's a simple `.ini` file that may be parsed with the usual parsers. The `xxxx` suffix is randomized. +* Make sure to ignore unknown `.ini` file keys in those files, so that we can easily extend the format later on. +* You'll find the question to ask the user in the `Message=` field in the `[Ask]` section. It is a single-line string in UTF-8, which might be internationalized (by the party that originally asks the question, not by the agent). +* You'll find an icon name (following the XDG icon naming spec) to show next to the message in the `Icon=` field in the `[Ask]` section +* You'll find the PID of the client asking the question in the `PID=` field in the `[Ask]` section (Before asking your question use `kill(PID, 0)` and ignore the file if this returns `ESRCH`; there's no need to show the data of this field but if you want to you may) +* `Echo=` specifies whether the input should be obscured. If this field is missing or is `Echo=0`, the input should not be shown. +* The socket to send the response to is configured via `Socket=` in the `[Ask]` section. It is a `AF_UNIX`/`SOCK_DGRAM` socket in the file system. +* Ignore files where the time specified in the `NotAfter=` field in the `[Ask]` section is in the past. The time is specified in usecs, and refers to the `CLOCK_MONOTONIC` clock. If `NotAfter=` is `0`, no such check should take place. +* Make sure to hide a password query dialog as soon as a) the `ask.xxxx` file is deleted, watch this with inotify. b) the `NotAfter=` time elapses, if it is set `!= 0`. +* Access to the socket is restricted to privileged users. To acquire the necessary privileges to send the answer back, consider using PolicyKit. In fact, the GNOME agent we ship does that, and you may simply piggyback on that, by executing "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 1 /path/to/socket`" or "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 0 /path/to/socket`" and writing the password to its standard input. Use '`1`' as argument if a password was entered by the user, or '`0`' if the user canceled the request. +* If you do not want to use PK ensure to acquire the necessary privileges in some other way and send a single datagram to the socket consisting of the password string either prefixed with "`+`" or with "`-`" depending on whether the password entry was successful or not. You may but don't have to include a final `NUL` byte in your message. + +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. + +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). + +If you write a system level agent a smart way to activate it is using systemd `.path` units. This will ensure that systemd will watch the `/run/systemd/ask-password` directory and spawn the agent as soon as that directory becomes non-empty. In fact, the console, wall and Plymouth agents are started like this. If systemd is used to maintain user sessions as well you can use a similar scheme to automatically spawn your user password agent as well. (As of this moment we have not switched any DE over to use systemd for session management, however.) diff --git a/docs/PORTABILITY_AND_STABILITY.md b/docs/PORTABILITY_AND_STABILITY.md index 95bfcb98d..4b138b593 100644 --- a/docs/PORTABILITY_AND_STABILITY.md +++ b/docs/PORTABILITY_AND_STABILITY.md @@ -87,7 +87,7 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy | [Boot Loader interface](https://systemd.io/BOOT_LOADER_INTERFACE) | EFI variables | yes | yes | gummiboot | yes | - | no | | [Service bus API](https://www.freedesktop.org/wiki/Software/systemd/dbus) | D-Bus | yes | yes | system-config-services | no | - | no | | [logind](https://www.freedesktop.org/wiki/Software/systemd/logind) | D-Bus | yes | yes | GNOME | no | - | no | -| [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, PolicyKit, ... | no | - | no | +| [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, polkit, ... | no | - | no | | [sd-daemon.h API](https://www.freedesktop.org/software/systemd/man/sd-daemon.html) | C Library or Drop-in | yes | yes | numerous | yes | - | yes | | [sd-id128.h API](https://www.freedesktop.org/software/systemd/man/sd-id128.html) | C Library | yes | yes | - | yes | - | no | | [sd-journal.h API](https://www.freedesktop.org/software/systemd/man/sd-journal.html) | C Library | yes | yes | - | maybe | - | no | @@ -98,7 +98,7 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy | [Unit file format](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) | File format | yes | yes | numerous | no | - | no | | [Network](https://www.freedesktop.org/software/systemd/man/systemd.network.html) & [Netdev file format](https://www.freedesktop.org/software/systemd/man/systemd.netdev.html) | File format | yes | yes | no | no | - | no | | [Link file format](https://www.freedesktop.org/software/systemd/man/systemd.link.html) | File format | yes | yes | no | no | - | no | -| [Journal File Format](https://www.freedesktop.org/wiki/Software/systemd/journal-files) | File format | yes | yes | - | maybe | - | no | +| [Journal File Format](https://systemd.io/JOURNAL_FILE_FORMAT) | File format | yes | yes | - | maybe | - | no | | [Journal Export Format](https://www.freedesktop.org/wiki/Software/systemd/export) | 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://www.freedesktop.org/wiki/Software/systemd/PasswordAgents) | Socket+Files | yes | yes | - | yes | - | no | diff --git a/docs/RANDOM_SEEDS.md b/docs/RANDOM_SEEDS.md index d3b68e967..da3fe40ba 100644 --- a/docs/RANDOM_SEEDS.md +++ b/docs/RANDOM_SEEDS.md @@ -118,7 +118,7 @@ requires random numbers as well, including for the following uses: * systemd maintains various hash tables internally. In order to harden them against [collision - attacks](https://rt.perl.org/Public/Bug/Display.html?CSRF_Token=165691af9ddaa95f653402f1b68de728) + attacks](https://www.cs.auckland.ac.nz/~mcw/Teaching/refs/misc/denial-of-service.pdf) they are seeded with random numbers. * At various places systemd needs random bytes for temporary file name @@ -212,10 +212,10 @@ boot, in order to ensure the entropy pool is filled up quickly. random-seed`](https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed)) a seed file with an initial seed is placed in a file `/loader/random-seed` in the ESP. In addition, an identically sized randomized EFI variable called - the the 'system token' is set, which is written to the machine's firmware - NVRAM. During boot, when `systemd-boot` finds both the random seed file and - the system token they are combined and hashed with SHA256 (in counter mode, - to generate sufficient data), to generate a new random seed file to store in + the 'system token' is set, which is written to the machine's firmware NVRAM. + During boot, when `systemd-boot` finds both the random seed file and the + system token they are combined and hashed with SHA256 (in counter mode, to + generate sufficient data), to generate a new random seed file to store in the ESP as well as a random seed to pass to the OS kernel. The new random seed file for the ESP is then written to the ESP, ensuring this is completed before the OS is invoked. Very early during initialization PID 1 will read @@ -257,7 +257,16 @@ boot, in order to ensure the entropy pool is filled up quickly. file. If done, `systemd-boot` will use the random seed file even if no system token is found in EFI variables. -With the three mechanisms described above it should be possible to provide +4. A kernel command line option `systemd.random_seed=` may be used to pass in a + base64 encoded seed to initialize the kernel's entropy pool from during + early service manager initialization. This option is only safe in testing + environments, as the random seed passed this way is accessible to + unprivileged programs via `/proc/cmdline`. Using this option outside of + testing environments is a security problem since cryptographic key material + derived from the entropy pool initialized with a seed accessible to + unprivileged programs should not be considered secret. + +With the four mechanisms described above it should be possible to provide early-boot entropy in most cases. Specifically: 1. On EFI systems, `systemd-boot`'s random seed logic should make sure good @@ -267,7 +276,8 @@ early-boot entropy in most cases. Specifically: 2. On virtualized systems, the early `virtio-rng` hookup should ensure entropy is available early on — as long as the VM environment provides virtualized RNG devices, which they really should all do in 2019. Complain to your - hosting provider if they don't. + 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 @@ -286,8 +296,9 @@ This primarily leaves two kind of systems in the cold: boot. Alternatively, consider implementing a solution similar to systemd-boot's random seed concept in your platform's boot loader. -2. Virtualized environments that lack both virtio-rng and RDRAND. Tough - luck. Talk to your hosting provider, and ask them to fix this. +2. Virtualized environments that lack both virtio-rng and RDRAND, outside of + test environments. Tough luck. Talk to your hosting provider, and ask them + to fix this. 3. Also note: if you deploy an image without any random seed and/or without installing any 'system token' in an EFI variable, as described above, this @@ -410,6 +421,10 @@ This primarily leaves two kind of systems in the cold: information to possibly gain too much information about the current state of the kernel's entropy pool. + That said, we actually do implement this with the `systemd.random_seed=` + kernel command line option. Don't use this outside of testing environments, + however, for the aforementioned reasons. + 12. *Why doesn't `systemd-boot` rewrite the 'system token' too each time when updating the random seed file stored in the ESP?* diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 0b34e5325..bd2915bab 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -6,4 +6,8 @@ layout: default # Reporting of Security Vulnerabilities -If you discover a security vulnerability, we'd appreciate a non-public disclosure. The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public. If you need to reach systemd developers in a non-public way, report the issue to the [systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list. The disclosure will be coordinated with distributions. +If you discover a security vulnerability, we'd appreciate a non-public disclosure. systemd developers can be contacted privately on the **[systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list**. The disclosure will be coordinated with distributions. + +(The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public.) + +Subscription to the systemd-security mailing list is open to **regular systemd contributors and people working in the security teams of various distributions**. Those conditions should be backed by publicly accessible information (ideally, a track of posts and commits from the mail address in question). If you fall into one of those categories and wish to be subscribed, submit a **[subscription request](https://www.redhat.com/mailman/listinfo/systemd-security)**. diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index 271d8ab1e..19944d08b 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -114,6 +114,7 @@ All execution-related settings are available for transient units. ✓ SupplementaryGroups= ✓ Nice= ✓ OOMScoreAdjust= +✓ CoredumpFilter= ✓ IOSchedulingClass= ✓ IOSchedulingPriority= ✓ CPUSchedulingPolicy= @@ -158,6 +159,9 @@ All execution-related settings are available for transient units. ✓ RestrictRealtime= ✓ RestrictSUIDSGID= ✓ RestrictAddressFamilies= +✓ RootHash= +✓ RootHashSignature= +✓ RootVerity= ✓ LockPersonality= ✓ LimitCPU= ✓ LimitFSIZE= @@ -284,37 +288,39 @@ All process killing settings are available for transient units: Most service unit settings are available for transient units. ``` -✓ PIDFile= +✓ BusName= ✓ ExecCondition= -✓ ExecStartPre= +✓ ExecReload= ✓ ExecStart= ✓ ExecStartPost= -✓ ExecReload= +✓ ExecStartPre= ✓ ExecStop= ✓ ExecStopPost= +✓ FileDescriptorStoreMax= +✓ GuessMainPID= +✓ NonBlocking= +✓ NotifyAccess= +✓ OOMPolicy= +✓ PIDFile= +✓ RemainAfterExit= +✓ Restart= +✓ RestartForceExitStatus= +✓ RestartPreventExitStatus= ✓ RestartSec= -✓ TimeoutStartSec= -✓ TimeoutStopSec= +✓ RootDirectoryStartOnly= +✓ RuntimeMaxSec= + Sockets= +✓ SuccessExitStatus= ✓ TimeoutAbortSec= ✓ TimeoutSec= -✓ RuntimeMaxSec= -✓ WatchdogSec= +✓ TimeoutStartFailureMode= +✓ TimeoutStartSec= +✓ TimeoutStopFailureMode= +✓ TimeoutStopSec= ✓ Type= -✓ Restart= -✓ RootDirectoryStartOnly= -✓ RemainAfterExit= -✓ GuessMainPID= -✓ RestartPreventExitStatus= -✓ RestartForceExitStatus= -✓ SuccessExitStatus= -✓ NonBlocking= -✓ BusName= -✓ FileDescriptorStoreMax= -✓ NotifyAccess= - Sockets= ✓ USBFunctionDescriptors= ✓ USBFunctionStrings= -✓ OOMPolicy= +✓ WatchdogSec= ``` ## Mount Unit Settings @@ -331,6 +337,7 @@ All mount unit settings are available to transient units: ✓ SloppyOptions= ✓ LazyUnmount= ✓ ForceUnmount= +✓ ReadWriteOnly= ``` ## Automount Unit Settings @@ -427,6 +434,7 @@ Most socket unit settings are available to transient units. ✓ Broadcast= ✓ PassCredentials= ✓ PassSecurity= +✓ PassPacketInfo= ✓ TCPCongestion= ✓ ReusePort= ✓ MessageQueueMaxMessages= diff --git a/docs/UIDS-GIDS.md b/docs/UIDS-GIDS.md index 255cc7132..67e6d083f 100644 --- a/docs/UIDS-GIDS.md +++ b/docs/UIDS-GIDS.md @@ -1,6 +1,6 @@ --- title: Users, Groups, UIDs and GIDs on systemd Systems -category: Concepts +category: Users, Groups and Home Directories layout: default --- @@ -132,7 +132,7 @@ but downstreams are strongly advised against doing that.) range is above the 16bit boundary. Moreover it's below the 31bit boundary, as some broken code (specifically: the kernel's `devpts` file system) erroneously considers UIDs signed integers, and hence can't deal with values - above 2^31. The `nss-mymachines` glibc NSS module will synthesize user + above 2^31. The `systemd-machined.service` service will synthesize user database records for all UIDs assigned to a running container from this range. @@ -240,14 +240,14 @@ the artifacts the container manager persistently leaves in the system. | 5 | `tty` group | `systemd` | `/etc/passwd` | | 6…999 | System users | Distributions | `/etc/passwd` | | 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… | -| 60001…60513 | Human Users (homed) | `systemd` | `nss-systemd` +| 60001…60513 | Human Users (homed) | `systemd` | `nss-systemd` | | 60514…61183 | Unused | | | | 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` | | 65520…65533 | Unused | | | | 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` | | 65535 | 16bit `(uid_t) -1` | Linux | | | 65536…524287 | Unused | | | -| 524288…1879048191 | Container UID ranges | `systemd` | `nss-mymachines` | +| 524288…1879048191 | Container UID ranges | `systemd` | `nss-systemd` | | 1879048192…2147483647 | Unused | | | | 2147483648…4294967294 | HIC SVNT LEONES | | | | 4294967295 | 32bit `(uid_t) -1` | Linux | | diff --git a/docs/USERDB_AND_DESKTOPS.md b/docs/USERDB_AND_DESKTOPS.md new file mode 100644 index 000000000..a19f746a2 --- /dev/null +++ b/docs/USERDB_AND_DESKTOPS.md @@ -0,0 +1,169 @@ +--- +title: systemd-homed and JSON User/Group Record Support in Desktop Environments +category: Users, Groups and Home Directories +layout: default +--- + +# `systemd-homed` and JSON User/Group Record Support in Desktop Environments + +Starting with version 245, systemd supports a new subsystem +[`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html) +for managing regular ("human") users and their home directories. Along with it +a new concept `userdb` got merged that brings rich, extensible JSON user/group +records, extending the classic UNIX/glibc NSS `struct passwd`/`struct group` +structures. Both additions are added in a fully backwards compatible way, +accessible through `getpwnam()`/`getgrnam()`/… (i.e. libc NSS) and PAM as +usual, meaning that for basic support no changes in the upper layers of the +stack (in particular desktop environments, such as GNOME or KDE) have to be +made. However, for better support a number of changes to desktop environments +are recommended. A few areas where that applies are discussed below. + +Before reading on, please read up on the basic concepts, specifically: + +* [Home Directories](https://systemd.io/HOME_DIRECTORY) +* [JSON User Records](https://systemd.io/USER_RECORD) +* [JSON Group Records](https://systemd.io/GROUP_RECORD) +* [User/Group Record Lookup API via Varlink](https://systemd.io/USER_GROUP_API) + +## Support for Suspending Home Directory Access during System Suspend + +One key feature of `systemd-homed` managed encrypted home directories is the +ability that access to them can be suspended automatically during system sleep, +removing any cryptographic key material from memory while doing so. This is +important in a world where most laptop users seldom shut down their computers +but most of the time just suspend them instead. Previously, the encryption keys +for the home directories remained in memory during system suspend, so that +sufficiently equipped attackers could read them from there and gain full access +to the device. By removing the key material from memory before suspend, and +re-requesting it on resume this attack vector can be closed down effectively. + +Supporting this mechanism requires support in the desktop environment, since +the encryption keys (i.e. the user's login password) need to be reacquired on +system resume, from a lock screen or similar. This lock screen must run in +system context, and cannot run in the user's own context, since otherwise it +might end up accessing the home directory of the user even though access to it +is temporarily suspended and thus will hang if attempted. + +It is suggested that desktop environments that implement lock screens run them +from system context, for example by switching back to the display manager, and +only revert back to the session after re-authentication via this system lock +screen (re-authentication in this case refers to passing the user's login +credentials to the usual PAM authentication hooks). Or in other words, when +going into system suspend it is recommended that GNOME Shell switches back to +the GNOME Display Manager login screen which now should double as screen lock, +and only switches back to the shell's UI after the user re-authenticated there. + +Note that this change in behavior is a good idea in any case, and does not +create any dependencies on `systemd-homed` or systemd-specific APIs. It's +simply a change of behavior regarding use of existing APIs, not a suggested +hook-up to a any new API. + +A display manager which supports this kind of out-of-context screen lock +operation needs to inform systemd-homed about this so that systemd-homed knows +that it is safe to suspend the user's home directory on suspend. This is done +via the `suspend=` argument to the +[`pam_systemd_home`](https://www.freedesktop.org/software/systemd/man/pam_systemd_home.html) +PAM module. A display manager should hence change its PAM stack configuration +to set this parameter to on. `systemd-homed` will not suspend home directories +if there's at least one active session of the user that does not support +suspending, as communicated via this parameter. + +## User Management UIs + +The rich user/group records `userdb` and `systemd-homed` support carry various +fields of relevance to UIs that manage the local user database or parts +thereof. In particular, most of the metadata `accounts-daemon` (also see below) +supports is directly available in these JSON records. Hence it makes sense for +any user management UI to expose them directly. + +`systemd-homed` exposes APIs to add, remove and make changes to local users via +D-Bus, with full [polkit](https://www.freedesktop.org/software/polkit/docs/latest/) +hook-up. On the command line this is exposed via the +`homectl` command. A graphical UI that exposes similar functionality would be +very useful, exposing the various new account settings, and in particular +providing a stream-lined UI for enrolling new-style authentication tokens such +as PKCS#11/YubiKey-style devices. (Ideally, if the user plugs in an +uninitialized YubiKey during operation it might be nice if the Desktop would +automatically ask if a key pair shall be written to it and the local account be +bound to it, `systemd-homed` provides enough YubiKey/PKCS#11 support to make +this a reality today; except that it will not take care of token +initialization). + +A strong point of `systemd-homed` is per-user resource management. In +particular disk space assignments are something that most likely should be +exposed in a user management UI. Various metadata fields are supplied allowing +exposure of disk space assignment "slider" UI. Note however that the file system +back-ends of `systemd-homed.service` have different feature sets. Specifically, +only btrfs has online file system shrinking support, ext4 only offline file +system shrinking support, and xfs no shrinking support at all (all three file +systems support online file system growing however). This means if the LUKS +back-end is used, disk space assignment cannot be instant for logged in users, +unless btrfs is used. + +Note that only `systemd-homed` provides an API for modifying/creating/deleting +users. The generic `userdb` subsystem (which might have other back-ends, besides +`systemd-homed`, for example LDAP or Windows) exclusively provides a read-only +interface. (This is unlikely to change, as the other back-ends might have very +different concepts of adding or modifying users, i.e. might not even have any +local concept for that at all). This means any user management UI that intends +to change (and not just view) user accounts should talk directly to +`systemd-homed` to make use of its features; there's no abstraction available +to support other back-ends under the same API. + +Unfortunately there's currently no documentation for the `systemd-homed` D-Bus +API. Consider using the `homectl` sources as guidelines for implementing a user +management UI. The JSON user/records are well documented however, see above, +and the D-Bus API provides limited introspection. + +## Relationship to `accounts-daemon` + +For a long time `accounts-daemon` has been included in Linux distributions +providing richer user accounts. The functionality of this daemon overlaps in +many areas with the functionality of `systemd-homed` or `userdb`, but there are +systematic differences, which means that `systemd-homed` cannot replace +`accounts-daemon` fully. Most importantly: `accounts-daemon` provides +"side-car" metadata for *any* type of user account, while `systemd-homed` only +provides additional metadata for the users it defines itself. In other words: +`accounts-daemon` will augment foreign accounts; `systemd-homed` cannot be used +to augment users defined elsewhere, for example in LDAP or as classic +`/etc/passwd` records. + +This probably means that for the time being, a user management UI (or other UI) +that wants to support rich user records with compatibility with the status quo +ante should probably talk to both `systemd-homed` and `accounts-daemon` at the +same time, and ignore `accounts-daemon`'s records if `systemd-homed` defines +them. While I (Lennart) personally believe in the long run `systemd-homed` is +the way to go for rich user records, any UI that wants to manage and support +rich records for classic records has to support `accounts-daemon` in parallel +for the time being. + +In the short term, it might make sense to also expose the `userdb` provided +records via `accounts-daemon`, so that clients of the latter can consume them +without changes. However, I think in the long run `accounts-daemon` should +probably be removed from the general stack, hence this sounds like a temporary +solution only. + +In case you wonder, there's no automatic mechanism for converting existing +users registered in `/etc/passwd` or LDAP to users managed by +`systemd-homed`. There's documentation for doing this manually though, see +[Converting Existing Users to systemd-homed managed +Users](https://systemd.io/CONVERTING_TO_HOMED). + +## Future Additions + +JSON user/group records are extensible, hence we can easily add any additional +fields desktop environments require. For example pattern-based authentication +is likely very useful on touch-based devices, and the user records should hence +learn them natively. Fields for other authentication mechanisms, such as +fingerprint authentication should be provided as well, eventually. + +It is planned to extend the `userdb` Varlink API to support look-ups by partial +user name and real name (GECOS) data, so that log-in screens can optionally +implement simple complete-as-you-type login screens. + +It is planned to extend the `systemd-homed` D-Bus API to instantly inform clients +about hardware associated with a specific user being plugged in, to which login +screens can listen in order to initiate authentication. Specifically, any +YubiKey-like security token plugged in that is associated with a local user +record should initiate authentication for that user, making typing in of the +username unnecessary. diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md index 21d498f5f..ca88b3a16 100644 --- a/docs/USER_GROUP_API.md +++ b/docs/USER_GROUP_API.md @@ -1,6 +1,6 @@ --- title: User/Group Record Lookup API via Varlink -category: Interfaces +category: Users, Groups and Home Directories layout: default --- @@ -95,7 +95,7 @@ services are listening there, that have special relevance: 2. `io.systemd.Multiplexer` → This service multiplexes client queries to all other running services. It's supposed to simplify client development: in order to look up or enumerate user/group records it's sufficient to talk to - one service instead of all of them in parallel. Note that it is not availabe + one service instead of all of them in parallel. Note that it is not available during earliest boot and final shutdown phases, hence for programs running in that context it is preferable to implement the parallel lookup themselves. @@ -108,7 +108,7 @@ example, introspection is not available, and the resolver logic is not used. ## Other Services -The `systemd` project provides two other services implementing this +The `systemd` project provides three other services implementing this interface. Specifically: 1. `io.systemd.DynamicUser` → This service is implemented by the service @@ -119,6 +119,10 @@ interface. Specifically: and provides records for the users and groups defined by the home directories it manages. +3. `io.systemd.Machine` → This service is implemented by + `systemd-machined.service` and provides records for the users and groups used + by local containers that use user namespacing. + Other projects are invited to implement these services too. For example it would make sense for LDAP/ActiveDirectory projects to implement these interfaces, which would provide them a way to do per-user resource management @@ -160,7 +164,7 @@ method GetUserRecord( service : string ) -> ( record : object, - incomplete : boolean + incomplete : bool ) method GetGroupRecord( @@ -169,7 +173,7 @@ method GetGroupRecord( service : string ) -> ( record : object, - incomplete : boolean + incomplete : bool ) method GetMemberships( @@ -185,6 +189,7 @@ error NoRecordFound() error BadService() error ServiceNotAvailable() error ConflictingRecordFound() +error EnumerationNotSupported() ``` The `GetUserRecord` method looks up or enumerates a user record. If the `uid` @@ -264,4 +269,11 @@ 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 that A is a member of B. +Looking up explicit users/groups by their name or UID/GID, or querying +user/group memberships must be supported by all services implementing these +interfaces. However, supporting enumeration (i.e. user/group lookups that may +result in more than one reply, because neither UID/GID nor name is specified) +is optional. Services which are asked for enumeration may return the +`EnumerationNotSupported` error in this case. + And that's really all there is to it. diff --git a/docs/USER_NAMES.md b/docs/USER_NAMES.md new file mode 100644 index 000000000..ec07b19f3 --- /dev/null +++ b/docs/USER_NAMES.md @@ -0,0 +1,169 @@ +--- +title: User/Group Name Syntax +category: Users, Groups and Home Directories +layout: default +--- + +# User/Group Name Syntax + +The precise set of allowed user and group names on Linux systems is weakly +defined. Depending on the distribution a different set of requirements and +restrictions on the syntax of user/group names are enforced — on some +distributions the accepted syntax is even configurable by the administrator. In +the interest of interoperability systemd enforces different rules when +processing users/group defined by other subsystems and when defining users/groups +itself, following the principle of "Be conservative in what you send, be +liberal in what you accept". Also in the interest of interoperability systemd +will enforce the same rules everywhere and not make them configurable or +distribution dependent. The precise rules are described below. + +Generally, the same rules apply for user as for group names. + +## Other Systems + +* On POSIX the set of [valid user + names](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_437) + is defined as [lower and upper case ASCII letters, digits, period, + underscore, and + hyphen](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282), + with the restriction that hyphen is not allowed as first character of the + user name. Interestingly no size limit is declared, i.e. in neither + direction, meaning that strictly speaking according to POSIX both the empty + string is a valid user name as well as a string of gigabytes in length. + +* Debian/Ubuntu based systems enforce the regular expression + `^[a-z][-a-z0-9]*$`, i.e. only lower case ASCII letters, digits and + hyphens. As first character only lowercase ASCII letters are allowed. This + regular expression is configurable by the administrator at runtime + though. This rule enforces a minimum length of one character but no maximum + length. + +* Upstream shadow-utils enforces the regular expression + `^[a-z_][a-z0-9_-]*[$]$`, i.e. is similar to the Debian/Ubuntu rule, but + allows underscores and hyphens, but the latter not as first character. Also, + an optional trailing dollar character is permitted. + +* Fedora/Red Hat based systems enforce the regular expression of + `^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9_.$-]?$`, i.e. a size limit of + 32 characters, with upper and lower case letters, digits, underscores, + hyphens and periods. No hyphen as first character though, and the last + character may be a dollar character. On top of that, `.` and `..` are not + allowed as user/group names. + +* sssd is known to generate user names with embedded `@` and white-space + characters, as well as non-ASCII (i.e. UTF-8) user/group names. + +* winbindd is known to generate user/group names with embedded `\` and + white-space characters, as well as non-ASCII (i.e. UTF-8) user/group names. + +Other operating systems enforce different rules; in this documentation we'll +focus on Linux systems only however, hence those are out of scope. That said, +software like Samba is frequently deployed on Linux for providing compatibility +with Windows systems; on such systems it might be wise to stick to user/group +names also valid according to Windows rules. + +## Rules systemd enforces + +Distilled from the above, below are the rules systemd enforces on user/group +names. An additional, common rule between both modes listed below is that empty +strings are not valid user/group names. + +Philosophically, the strict mode described below enforces an allow list of +what's allowed and prohibits everything else, while the relaxed mode described +below implements a deny list of what's not allowed and permits everything else. + +### Strict mode + +Strict user/group name syntax is enforced whenever a systemd component is used +to register a user or group in the system, for example a system user/group +using +[`systemd-sysusers.service`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.html) +or a regular user with +[`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.html). + +In strict mode, only uppercase and lowercase characters are allowed, as well as +digits, underscores and hyphens. The first character may not be a digit or +hyphen. A size limit is enforced: the minimum of `sysconf(_SC_LOGIN_NAME_MAX)` +(typically 256 on Linux; rationale: this is how POSIX suggests to detect the +limit), `UT_NAMESIZE-1` (typically 31 on Linux; rationale: names longer than +this cannot correctly appear in `utmp`/`wtmp` and create ambiguity with login +accounting) and `FILENAME_MAX` (4096 on Linux; rationale: user names typically +appear in directory names, i.e. the home directory), thus MIN(256, 31, 4096) = +31. + +Note that these rules are both more strict and more relaxed than all of the +rules enforced by other systems listed above. A user/group name conforming to +systemd's strict rules will not necessarily pass a test by the rules enforced +by these other subsystems. + +Written as regular expression the above is: `^[a-zA-Z_][a-zA-Z0-9_-]{0,30}$` + +### Relaxed mode + +Relaxed user/group name syntax is enforced whenever a systemd component accepts +and makes use of user/group names registered by other (non-systemd) +components of the system, for example in +[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.html). + +Relaxed syntax is also enforced by the `User=` setting in service unit files, +i.e. for system services used for running services. Since these users may be +registered by a variety of tools relaxed mode is used, but since the primary +purpose of these users is to run a system service and thus a job for systemd a +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) + +* No names consisting fully of digits (rationale: avoid confusion with numeric + UID/GID specifications) + +* Similar, no names consisting of an initial hyphen and otherwise entirely made + up of digits (rationale: avoid confusion with negative, numeric UID/GID + specifications, e.g. `-1`) + +* No strings that do not qualify as valid UTF-8 (rationale: we want to be able + to embed these strings in JSON, with permits only valid UTF-8 in its strings; + user names using other character sets, such as JIS/Shift-JIS will cause + validation errors) + +* No control characters (i.e. characters in ASCII range 1…31; rationale: they + tend to have special meaning when output on a terminal in other contexts, + moreover the newline character — as a specific control character — is used as + record separator in `/etc/passwd`, and hence it's crucial to avoid + ambiguities here) + +* No colon characters (rationale: it is used as field separator in `/etc/passwd`) + +* The two strings `.` and `..` are not permitted, as these have special meaning + in file system paths, and user names are frequently included in file system + paths, in particular for the purpose of home directories. + +* Similar, no slashes, as these have special meaning in file system paths + +* No leading or trailing white-space is permitted; and hence no user/group names + consisting of white-space only either (rationale: this typically indicates + parsing errors, and creates confusion since not visible on screen) + +Note that these relaxed rules are implied by the strict rules above, i.e. all +user/group names accepted by the strict rules are also accepted by the relaxed +rules, but not vice versa. + +Note that this relaxed mode does not refuse a couple of very questionable +syntaxes. For example it permits a leading or embedded period. A leading period +is problematic because the matching home directory would typically be hidden +from the user's/administrator's view. An embedded period is problematic since +it creates ambiguity in traditional `chown` syntax (which is still accepted +today) that uses it to separate user and group names in the command's +parameter: without consulting the user/group databases it is not possible to +determine if a `chown` invocation would change just the owning user or both the +owning user and group. It also allows embedding `@` (which is confusing to +MTAs). + +## Common Core + +Combining all rules listed above, user/group names that shall be considered +valid in all systemd contexts and on all Linux systems should match the +following regular expression (at least according to our understanding): + +`^[a-z][a-z0-9-]{0,30}$` diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md index 9828cd50b..f6d22c217 100644 --- a/docs/USER_RECORD.md +++ b/docs/USER_RECORD.md @@ -1,6 +1,6 @@ --- title: JSON User Records -category: Interfaces +category: Users, Groups and Home Directories layout: default --- @@ -14,7 +14,7 @@ pairs, encoded as JSON. Specifically: 1. [`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html) manages `human` user home directories and embeds these JSON records directly in the home directory images (see [Home - Directories](https://systemd.io/HOME_DIRECTORY)) for details. + Directories](https://systemd.io/HOME_DIRECTORY) for details). 2. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html) processes these JSON records for users that log in, and applies various @@ -180,7 +180,7 @@ strictly local context and without signatures doesn't have to deal with the `perMachine` or `binding` sections and can include its data exclusively in the regular section. A service that uses a separate, private channel for authenticating users (or that doesn't have a concept of authentication at all) -does not need to to be concerned with the `secret` section of user records, as +does not need to be concerned with the `secret` section of user records, as the fields included therein are only useful when executing authentication operations natively against JSON user records. @@ -455,6 +455,10 @@ storage. If false and `luks` storage is used turns this behavior off. In addition, depending on this setting an `FITRIM` or `fallocate()` operation is executed to make sure the image matches the selected option. +`luksOfflineDiscard` → A boolean. Similar to `luksDiscard`, it controls whether +to trim/allocate the file system/backing file when deactivating the home +directory. + `luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism. `luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism. @@ -542,7 +546,12 @@ below). It's undefined how precise the URI is: during log-in it is tested against all plugged in security tokens and if there's exactly one matching private key found with it it is used. -`privileged` → An object, which contains the fields of he `privileged` section +`fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2 +credential ID that shell 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`. + +`privileged` → An object, which contains the fields of the `privileged` section of the user record, see below. `perMachine` → An array of objects, which contain the `perMachine` section of @@ -578,7 +587,7 @@ restrictive access semantics. The following fields are currently defined: be a string like "What's the name of your first pet?", but is entirely for the user to choose. -`hashPassword` → An array of strings, each containing a hashed UNIX password +`hashedPassword` → An array of strings, each containing a hashed UNIX password string, in the format [`crypt(3)`](http://man7.org/linux/man-pages/man3/crypt.3.html) generates. This corresponds with `sp_pwdp` field of `struct spwd` (and in a way the `pw_passwd` @@ -590,7 +599,7 @@ as the lines in the traditional `~/.ssh/authorized_key` file. `pkcs11EncryptedKey` → An array of objects. Each element of the array should be an object consisting of three string fields: `uri` shall contain a PKCS#11 -security token URI, `data` shall contain a Base64 encoded encrypted key and +security token URI, `data` shall contain a Base64-encoded encrypted key and `hashedPassword` shall contain a UNIX password hash to test the key against. Authenticating with a security token against this account shall work as follows: the encrypted secret key is converted from its Base64 @@ -598,13 +607,29 @@ representation into binary, then decrypted with the PKCS#11 `C_Decrypt()` function of the PKCS#11 module referenced by the specified URI, using the private key found on the same token. The resulting decrypted key is then Base64-encoded and tested against the specified UNIX hashed password. The -Base64-enceded decrypted key may also be used to unlock further resources +Base64-encoded decrypted key may also be used to unlock further resources during log-in, for example the LUKS or `fscrypt` storage backend. It is generally recommended that for each entry in `pkcs11EncryptedKey` there's also a matching one in `pkcs11TokenUri` and vice versa, with the same URI, appearing in the same order, but this should not be required by applications processing user records. +`fido2HmacSalt` → An array of objects, implementing authentication support with +FIDO2 devices that implement the `hmac-secret` extension. Each element of the +array should be an object consisting of three string fields: `credential`, +`salt`, `hashedPassword`. The first two shall contain Base64-encoded binary +data: the FIDO2 credential ID and the salt value to pass to the FIDO2 +device. During authentication this salt along with the credential ID is sent to +the FIDO2 token, which will HMAC hash the salt with its internal secret key and +return the result. This resulting binary key should then be Base64-encoded and +used as string password for the further layers of the stack. The +`hashedPassword` field of the `fido2HmacSalt` field shall be a UNIX password +hash to test this derived secret key against for authentication. It is +generally recommended that for each entry in `fido2HmacSalt` there's also a +matching one in `fido2HmacCredential`, and vice versa, with the same credential +ID, appearing in the same order, but this should not be required by +applications processing user records. + ## Fields in the `perMachine` section As mentioned, the `perMachine` section contains settings that shall apply to @@ -648,12 +673,13 @@ that may be used in this section are identical to the equally named ones in the `mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`, `cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`, `fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`, -`luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`, -`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`, -`luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, `rateLimitBurst`, -`enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`, -`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`, -`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`. +`luksOfflineDiscard`, `luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, +`luksPbkdfHashAlgorithm`, `luksPbkdfType`, `luksPbkdfTimeCostUSec`, +`luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, +`rateLimitBurst`, `enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, +`killProcesses`, `passwordChangeMinUSec`, `passwordChangeMaxUSec`, +`passwordChangeWarnUSec`, `passwordChangeInactiveUSec`, `passwordChangeNow`, +`pkcs11TokenUri`, `fido2HmacCredential`. ## Fields in the `binding` section @@ -805,7 +831,7 @@ public key. The `signature` field in the top-level user record object is an array of objects. Each object encapsulates one signature and has two fields: `data` and `key` (both are strings). The `data` field contains the actual signature, -encoded in base64, the `key` field contains a copy of the public key whose +encoded in Base64, the `key` field contains a copy of the public key whose private key was used to make the signature, in PEM format. Currently only signatures with Ed25519 keys are defined. @@ -859,13 +885,20 @@ The `secret` field of the top-level user record contains the following fields: `password` → an array of strings, each containing a plain text password. -`pkcs11Pin` → an array of strings, each containing a plain text PIN, suitable -for unlocking PKCS#11 security tokens that require that. +`tokenPin` → an array of strings, each containing a plain text PIN, suitable +for unlocking security tokens that require that. (The field `pkcs11Pin` should +be considered a compatibility alias for this field, and merged with `tokenPin` +in case both are set.) `pkcs11ProtectedAuthenticationPathPermitted` → a boolean. If set to true allows the receiver to use the PKCS#11 "protected authentication path" (i.e. a physical button/touch element on the security token) for authenticating the -user. If false or unset authentication this way shall not be attempted. +user. If false or unset, authentication this way shall not be attempted. + +`fido2UserPresencePermitted` → a boolean. If set to true allows the receiver to +use the FIDO2 "user presence" flag. This is similar to the concept of +`pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2 concept +behind it. If false or unset authentication this way shall not be attempted. ## Mapping to `struct passwd` and `struct spwd` diff --git a/factory/etc/nsswitch.conf b/factory/etc/nsswitch.conf index e7365cd14..da74b19d9 100644 --- a/factory/etc/nsswitch.conf +++ b/factory/etc/nsswitch.conf @@ -1,7 +1,7 @@ # This file is part of systemd. -passwd: compat mymachines systemd -group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd +passwd: compat systemd +group: compat [SUCCESS=merge] systemd shadow: compat hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname diff --git a/fuzzbuzz.yaml b/fuzzbuzz.yaml deleted file mode 100644 index 2cd1763a6..000000000 --- a/fuzzbuzz.yaml +++ /dev/null @@ -1,33 +0,0 @@ -base: ubuntu:16.04 -language: c -setup: -- sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list" -- sudo apt-get update -y -- sudo apt-get build-dep -y systemd -- sudo apt-get install -y python3-pip -- sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev -- pip3 install meson ninja -- export PATH="$HOME/.local/bin/:$PATH" -- CC=$FUZZ_CC CXX=$FUZZ_CXX meson -Dfuzzbuzz=true -Dfuzzbuzz-engine-dir=$(dirname "$FUZZ_ENGINE") -Dfuzzbuzz-engine=$(cut -d. -f1 <(basename "$FUZZ_ENGINE")) -Db_lundef=false ./build -- ninja -v -C ./build fuzzers -environment: -targets: -- name: fuzz-compress - harness: - binary: ./build/fuzz-compress -- name: fuzz-unit-file - harness: - binary: ./build/fuzz-unit-file - corpus: ./test/fuzz/fuzz-unit-file -- name: fuzz-journald-syslog - harness: - binary: ./build/fuzz-journald-syslog - corpus: ./test/fuzz/fuzz-journald-syslog -- name: fuzz-netdev-parser - harness: - binary: ./build/fuzz-netdev-parser - corpus: ./test/fuzz/fuzz-netdev-parser -- name: fuzz-network-parser - harness: - binary: ./build/fuzz-network-parser - corpus: ./test/fuzz/fuzz-network-parser diff --git a/hwdb.d/60-autosuspend.hwdb b/hwdb.d/60-autosuspend.hwdb new file mode 100644 index 000000000..e742a33ea --- /dev/null +++ b/hwdb.d/60-autosuspend.hwdb @@ -0,0 +1,54 @@ +# This file is part of systemd. +# +# The lookup keys are $MODALIAS strings, see udev's hwdb builtin. +# +# Match string formats: +# : +# +# pci:vd +# usb:vp +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/61-autosuspend-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# systemd-hwdb update +# udevadm trigger /dev/… +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info +# the device. +# +# Allowed properties are: +# ID_AUTOSUSPEND=1 + +# +# Sort by brand, model + +######################################### +# Alcor +######################################### + +# AU9540 Smartcard Reader +usb:v058Fp9540* + ID_AUTOSUSPEND=1 + +######################################### +# QEMU +######################################### + +# Emulated USB HID devices +usb:v0627p0001:*QEMU USB Keyboard* +usb:v0627p0001:*QEMU USB Mouse* +usb:v0627p0001:*QEMU USB Tablet* + ID_AUTOSUSPEND=1 + +######################################### +# Wacom +######################################### + +usb:v056Ap51A0* + ID_AUTOSUSPEND=1 diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb index 7b08e2643..84d997ebd 100644 --- a/hwdb.d/60-keyboard.hwdb +++ b/hwdb.d/60-keyboard.hwdb @@ -598,8 +598,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHDX9494NR:pvr* # HP EliteBook 725 G2 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPLicrice:pvr* -# HP EliteBook 840 G1 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook840G1:pvr* +# HP EliteBook +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:pvr* # HP ProBook 440 G2 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:pvr* # several HP ProBooks 4xx @@ -608,8 +609,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:pvr* # HP ZBook evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:pvr* -# Elitebook x360 1040 G6 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pn*EliteBook*x3601040G6:pvr* KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute # HP ZBook 15 G2 @@ -628,7 +627,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnHPZBookStudioG4:pvr* # HP Folio 1040g2 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr* - KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute KEYBOARD_KEY_d8=!f23 # touchpad off KEYBOARD_KEY_d9=!f22 # touchpad on diff --git a/hwdb.d/60-sensor.hwdb b/hwdb.d/60-sensor.hwdb index 576b314d3..092d356b6 100644 --- a/hwdb.d/60-sensor.hwdb +++ b/hwdb.d/60-sensor.hwdb @@ -680,6 +680,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTREKSTOR:pnPrimetabT13B:* sensor:modalias:acpi:BOSC0200*:dmi:*:svnTrekStor*:pnSurfTabtwin11.6:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 +# alternative version of Trekstor's SurfTab Twin 11.6 +sensor:modalias:acpi:BOSC0200*:dmi:*:bvrTP15-VT5.2.1.3:*:svnTrekStor*:pnSurfTabtwin11.6:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1 + sensor:modalias:acpi:KIOX010A*:dmi:*:svnTREKSTOR:pnPrimebookC11B:* sensor:modalias:acpi:KIOX010A*:dmi:*:svnTREKSTOR:pnPRIMEBOOKC11B:* ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1 diff --git a/hwdb.d/meson.build b/hwdb.d/meson.build index 4df6dabf8..5c77387a2 100644 --- a/hwdb.d/meson.build +++ b/hwdb.d/meson.build @@ -1,6 +1,9 @@ # SPDX-License-Identifier: LGPL-2.1+ -hwdb_files = files(''' +# Those files right now are not supported by the grammar. Also, +# they are very long but quite repetitive and the parser is not very fast. +# So we don't "test" them. +hwdb_files_notest = files(''' 20-pci-vendor-model.hwdb 20-pci-classes.hwdb 20-usb-vendor-model.hwdb @@ -12,6 +15,10 @@ hwdb_files = files(''' 20-OUI.hwdb 20-net-ifname.hwdb 20-vmbus-class.hwdb +'''.split()) + +hwdb_files_test = files(''' + 60-autosuspend.hwdb 60-evdev.hwdb 60-input-id.hwdb 60-keyboard.hwdb @@ -23,7 +30,16 @@ hwdb_files = files(''' '''.split()) if conf.get('ENABLE_HWDB') == 1 - install_data(hwdb_files, + auto_suspend_rules = custom_target( + '60-autosuspend-chromiumos.hwdb', + output : '60-autosuspend-chromiumos.hwdb', + command : make_autosuspend_rules_py, + capture : true, + install : true, + install_dir: udevhwdbdir) + + install_data(hwdb_files_notest, + hwdb_files_test, install_dir : udevhwdbdir) meson.add_install_script('sh', '-c', @@ -32,15 +48,15 @@ if conf.get('ENABLE_HWDB') == 1 meson.add_install_script('sh', '-c', 'test -n "$DESTDIR" || @0@/systemd-hwdb update' .format(rootbindir)) -endif -############################################################ - -parse_hwdb_py = find_program('parse_hwdb.py') -if want_tests != 'false' - test('parse-hwdb', - parse_hwdb_py, - timeout : 90) + if want_tests != 'false' + parse_hwdb_py = find_program('parse_hwdb.py') + test('parse-hwdb', + parse_hwdb_py, + args : [hwdb_files_test, + auto_suspend_rules], + timeout : 90) + endif endif ############################################################ diff --git a/hwdb.d/parse_hwdb.py b/hwdb.d/parse_hwdb.py index 579c45fda..025133416 100755 --- a/hwdb.d/parse_hwdb.py +++ b/hwdb.d/parse_hwdb.py @@ -59,6 +59,7 @@ REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER)) SIGNED_REAL = Combine(Optional(Word('-+')) + REAL) UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_') +# Those patterns are used in type-specific matches TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'), 'evdev': ('name', 'atkbd', 'input'), 'id-input': ('modalias'), @@ -68,13 +69,30 @@ TYPES = {'mouse': ('usb', 'bluetooth', 'ps2', '*'), 'sensor': ('modalias', ), } +# Patterns that are used to set general properties on a device +GENERAL_MATCHES = {'acpi', + 'bluetooth', + 'usb', + 'pci', + 'sdio', + 'vmbus', + 'OUI', + } + +def upperhex_word(length): + return Word(nums + 'ABCDEF', exact=length) + @lru_cache() def hwdb_grammar(): ParserElement.setDefaultWhitespaceChars('') prefix = Or(category + ':' + Or(conn) + ':' for category, conn in TYPES.items()) - matchline = Combine(prefix + Word(printables + ' ' + '®')) + EOL + + matchline_typed = Combine(prefix + Word(printables + ' ' + '®')) + matchline_general = Combine(Or(GENERAL_MATCHES) + ':' + Word(printables + ' ' + '®')) + matchline = (matchline_typed | matchline_general) + EOL + propertyline = (White(' ', exact=1).suppress() + Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.!-;, "') - Optional(pythonStyleComment)) + EOL) @@ -102,6 +120,7 @@ def property_grammar(): ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER), ('MOUSE_WHEEL_CLICK_COUNT', INTEGER), ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER), + ('ID_AUTOSUSPEND', Literal('1')), ('ID_INPUT', Literal('1')), ('ID_INPUT_ACCELEROMETER', Literal('1')), ('ID_INPUT_JOYSTICK', Literal('1')), @@ -115,8 +134,6 @@ def property_grammar(): ('ID_INPUT_TOUCHPAD', Literal('1')), ('ID_INPUT_TOUCHSCREEN', Literal('1')), ('ID_INPUT_TRACKBALL', Literal('1')), - ('MOUSE_WHEEL_TILT_HORIZONTAL', Literal('1')), - ('MOUSE_WHEEL_TILT_VERTICAL', Literal('1')), ('POINTINGSTICK_SENSITIVITY', INTEGER), ('POINTINGSTICK_CONST_ACCEL', REAL), ('ID_INPUT_JOYSTICK_INTEGRATION', Or(('internal', 'external'))), @@ -166,8 +183,27 @@ def parse(fname): return [] return [convert_properties(g) for g in parsed.GROUPS] -def check_match_uniqueness(groups): +def check_matches(groups): matches = sum((group[0] for group in groups), []) + + # This is a partial check. The other cases could be also done, but those + # two are most commonly wrong. + grammars = { 'usb' : 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4)), + 'pci' : 'v' + upperhex_word(8) + Optional('d' + upperhex_word(8)), + } + + for match in matches: + prefix, rest = match.split(':', maxsplit=1) + gr = grammars.get(prefix) + if gr: + try: + gr.parseString(rest) + except ParseBaseException as e: + error('Pattern {!r} is invalid: {}', rest, e) + continue + if rest[-1] not in '*:': + error('pattern {} does not end with "*" or ":"', match) + matches.sort() prev = None for match in matches: @@ -242,7 +278,7 @@ if __name__ == '__main__': for fname in args: groups = parse(fname) print_summary(fname, groups) - check_match_uniqueness(groups) + check_matches(groups) check_properties(groups) sys.exit(ERROR) diff --git a/man/bootctl.xml b/man/bootctl.xml index c038c4686..6db048b63 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -102,7 +102,7 @@ - VALUE + STRING When called without the optional argument, prints the current value of the SystemdOptions EFI variable. When called with an argument, sets the @@ -111,6 +111,17 @@ for the meaning of that variable. + + BOOL + + Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a + boolean argument which controls whether to show the firmware setup on next system reboot. If the + argument is omitted shows the current status of the flag, or whether the flag is supported. This + controls the same flag as systemctl reboot --firmware-setup, but is more + low-level and allows setting the flag independently from actually requesting a + reboot. + + diff --git a/man/bootup.xml b/man/bootup.xml index 5a0a3c287..26f762bf8 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -32,7 +32,7 @@ of firmware, this firmware may also load the kernel directly. The kernel (optionally) mounts an in-memory file system, often generated by - dracut8, + dracut8, which looks for the root file system. Nowadays this is usually implemented as an initramfs — a compressed archive which is extracted when the kernel boots up into a lightweight in-memory file system based on tmpfs, but in the past normal file systems using an in-memory block device (ramdisk) were used, and the @@ -170,7 +170,7 @@ emergency.service | | | user units. For non-graphical sessions, default.target is used. Whenever the user logs into a graphical session, the login manager will start the graphical-session.target target that is used to pull in units required for the - grahpical session. A number of targets (shown on the right side) are started when specific hardware is + graphical session. A number of targets (shown on the right side) are started when specific hardware is available to the user. @@ -192,7 +192,7 @@ emergency.service | | | v graphical-session-pre.target (various user services) | (printers) | v | - | (services for the graphical sesion) v + | (services for the graphical session) v | | printer.target v v default.target graphical-session.target @@ -349,7 +349,7 @@ systemd-reboot.service systemd-poweroff.service systemd-halt.service syste systemd.special7, systemd.target5, systemd-halt.service8, - dracut8 + dracut8 diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml index 81cdc3389..46da7741c 100644 --- a/man/coredump.conf.xml +++ b/man/coredump.conf.xml @@ -49,7 +49,7 @@ Options All options are configured in the - [Coredump] section: + [Coredump] section: diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml index ab5dffdfb..150d410c2 100644 --- a/man/coredumpctl.xml +++ b/man/coredumpctl.xml @@ -52,8 +52,8 @@ matching specified characteristics. If no command is specified, this is the implied default. - The output is designed to be human readable and contains list contains - a table with the following columns: + The output is designed to be human readable and contains a table with the following + columns: TIME @@ -134,7 +134,7 @@ Invoke a debugger on the last core dump matching specified characteristics. By default, - gdb1 + gdb1 will be used. This may be changed using the option or the $SYSTEMD_DEBUGGER environment variable. @@ -213,7 +213,7 @@ Use the given debugger for the debug command. If not given and $SYSTEMD_DEBUGGER is unset, then - gdb1 + gdb1 will be used. diff --git a/man/crypttab.xml b/man/crypttab.xml index 9b6fffd15..ee54499bf 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -41,7 +41,7 @@ character are ignored. Each of the remaining lines describes one encrypted block device. Fields are delimited by white space. - Each line is in the formname encrypted-device password options + Each line is in the formvolume-name encrypted-device key-file options The first two fields are mandatory, the remaining two are optional. @@ -53,24 +53,22 @@ it is opened as a LUKS device; otherwise, it is assumed to be in raw dm-crypt (plain mode) format. - The first field contains the name of the resulting encrypted - block device; the device is set up within - /dev/mapper/. + The first field contains the name of the resulting encrypted volume; its block device is set up + below /dev/mapper/. The second field contains a path to the underlying block device or file, or a specification of a block device via UUID= followed by the UUID. - The third field specifies the encryption password. If the - field is not present or the password is set to - none or -, the password has - to be manually entered during system boot. Otherwise, the field is - interpreted as an absolute path to a file containing the encryption - password. For swap encryption, /dev/urandom - or the hardware device /dev/hw_random can be - used as the password file; using /dev/random - may prevent boot completion if the system does not have enough - entropy to generate a truly random encryption key. + The third field specifies an absolute path to a file to read the encryption key from. Optionally, + the path may be followed by : and an fstab device specification (e.g. starting with + LABEL= or similar); in which case, the path is relative to the device file system + root. If the field is not present or set to none or -, a key file + named after the volume to unlock (i.e. the first column of the line), suffixed with + .key is automatically loaded from the /etc/cryptsetup-keys.d/ + and /run/cryptsetup-keys.d/ directories, if present. Otherwise, the password has to + be manually entered during system boot. For swap encryption, /dev/urandom may be + used as key file. The fourth field, if present, is a comma-delimited list of options. The following options are recognized: @@ -138,6 +136,15 @@ size is then given by the key size. + + + + If enabled, the specified key file is erased after the volume is activated or when + activation fails. This is in particular useful when the key file is only acquired transiently before + activation (e.g. via a file in /run/, generated by a service running before + activation), and shall be removed after use. Defaults to off. + + @@ -172,6 +179,13 @@ . + + + + Decrypt Bitlocker drive. Encryption parameters + are deduced by cryptsetup from Bitlocker header. + + @@ -241,6 +255,7 @@ Perform encryption using the same cpu that IO was submitted on. The default is to use an unbound workqueue so that encryption work is automatically balanced between available CPUs. + This requires kernel 4.0 or newer. @@ -249,9 +264,10 @@ Disable offloading writes to a separate thread after encryption. There are some - situations where offloading write bios from the encryption threads to a single thread degrades - performance significantly. The default is to offload write bios to the same thread because it benefits - CFQ to have writes submitted using the same context. + situations where offloading write requests from the encryption threads to a dedicated thread degrades + performance significantly. The default is to offload write requests to a dedicated thread because it + benefits the CFQ scheduler to have writes submitted using the same context. + This requires kernel 4.0 or newer. @@ -388,18 +404,17 @@ - + - The encrypted block device will be prepared - for using it as /tmp; it will be - formatted using - mke2fs8. - This option implies . + The encrypted block device will be prepared for using it as + /tmp/; it will be formatted using mkfs8. Takes + a file system type as argument, such as ext4, xfs or + btrfs. If no argument is specified defaults to ext4. This + option implies . - WARNING: Using the option will - destroy the contents of the named partition during every boot, - so make sure the underlying block device is specified - correctly. + WARNING: Using the option will destroy the contents of the named partition + during every boot, so make sure the underlying block device is specified correctly. @@ -431,6 +446,15 @@ before it is used to unlock the LUKS volume. + + + + Takes a boolean argument. If enabled, right before asking the user for a password it + is first attempted to unlock the volume with an empty password. This is useful for systems that are + initialized with an encrypted volume with only an empty password set, which shall be replaced with a + suitable password during first boot, but after activation. + + @@ -490,7 +514,8 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10sThe PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA decryption keys. Here's an example how to set up a Yubikey security token for this purpose, using - ykman from the yubikey-manager project: + ykmap1 + from the yubikey-manager project: diff --git a/man/custom-entities.ent.in b/man/custom-entities.ent.in index 85805777a..03fe05f1b 100644 --- a/man/custom-entities.ent.in +++ b/man/custom-entities.ent.in @@ -1,11 +1,12 @@ - - - - + + + + + diff --git a/man/daemon.xml b/man/daemon.xml index 52b388360..072529eee 100644 --- a/man/daemon.xml +++ b/man/daemon.xml @@ -357,7 +357,7 @@ special target unit sockets.target. It is recommended to place a WantedBy=sockets.target directive in the - [Install] section to automatically add such a + [Install] section to automatically add such a dependency on installation of a socket unit. Unless DefaultDependencies=no is set, the necessary ordering dependencies are implicitly created for all socket @@ -520,7 +520,7 @@ operating system-independent. Make sure to include an - [Install] section including installation + [Install] section including installation information for the unit file. See systemd.unit5 for details. To activate your service on boot, make sure to diff --git a/man/directives-template.xml b/man/directives-template.xml new file mode 100644 index 000000000..e0063e051 --- /dev/null +++ b/man/directives-template.xml @@ -0,0 +1,199 @@ + + + + + systemd.directives + systemd + + + + systemd.directives + 7 + + + + systemd.directives + Index of configuration directives + + + + Unit directives + + Directives for configuring units, used in unit files. + + + + + + Options on the kernel command line + + Kernel boot options for configuring the behaviour of the systemd process. + + + + + + Environment variables + + Environment variables understood by the systemd manager and other programs and environment + variable-compatible settings. + + + + + + EFI variables + + EFI variables understood by + systemd-boot7 + and other programs. + + + + + + Home Area/User Account directives + + Directives for configuring home areas and user accounts via + systemd-homed.service8. + + + + + + UDEV directives + + Directives for configuring systemd units through the udev database. + + + + + + Network directives + + Directives for configuring network links through the net-setup-link udev builtin and networks + through systemd-networkd. + + + + + + Journal fields + + Fields in the journal events with a well known meaning. + + + + + + PAM configuration directives + + Directives for configuring PAM behaviour. + + + + + + <filename>/etc/crypttab</filename> and + <filename>/etc/fstab</filename> options + + Options which influence mounted filesystems and encrypted volumes. + + + + + + <citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry> + directives + + Directives for configuring systemd-nspawn containers. + + + + + + Program configuration options + + Directives for configuring the behaviour of the systemd process and other tools through + configuration files. + + + + + + Command line options + + Command-line options accepted by programs in the systemd suite. + + + + + + Constants + + Various constant used and/or defined by systemd. + + + + + + Miscellaneous options and directives + + Other configuration elements which don't fit in any of the above groups. + + + + + + Specifiers + + Short strings which are substituted in configuration directives. + + + + + + Files and directories + + Paths and file names referred to in the documentation. + + + + + + D-Bus interfaces + + Interfaces exposed over D-Bus. + + + + + + D-Bus methods + + Methods exposed in the D-Bus interface. + + + + + + D-Bus properties + + Properties exposed in the D-Bus interface. + + + + + + D-Bus signals + + Signals emitted in the D-Bus interface. + + + + + + Colophon + + + diff --git a/man/environment.d.xml b/man/environment.d.xml index 17c2c505e..0f53b0fef 100644 --- a/man/environment.d.xml +++ b/man/environment.d.xml @@ -22,7 +22,7 @@ environment.d - Definition of user session environment + Definition of user service environment @@ -36,8 +36,8 @@ Description - The environment.d directories contain a list of environment variable - assignments for services started by the systemd user instance. + Configuration files in the environment.d/ directories contain lists of + environment variable assignments for services started by the systemd user instance. systemd-environment-d-generator8 parses them and updates the environment exported by the systemd user instance. See below for an discussion of which processes inherit those variables. @@ -58,7 +58,6 @@ variable assignments, separated by newlines. The right hand side of these assignments may reference previously defined environment variables, using the ${OTHER_KEY} and $OTHER_KEY format. It is also possible to use - ${FOO:-DEFAULT_VALUE} to expand in the same way as ${FOO} unless the expansion would be empty, in which case it expands to DEFAULT_VALUE, @@ -95,7 +94,7 @@ Environment variables exported by the user manager (systemd --user instance started in the user@uid.service system service) apply to any services started by that manager. In particular, this may include services which run user shells. For - example in the Gnome environment, the graphical terminal emulator runs as the + example in the GNOME environment, the graphical terminal emulator runs as the gnome-terminal-server.service user unit, which in turn runs the user shell, so that shell will inherit environment variables exported by the user manager. For other instances of the shell, not launched by the user manager, the environment they inherit is defined by the program that starts diff --git a/man/file-hierarchy.xml b/man/file-hierarchy.xml index 497cb580c..d5899dc36 100644 --- a/man/file-hierarchy.xml +++ b/man/file-hierarchy.xml @@ -648,7 +648,7 @@ /usr/share/ hierarchy to the locations defined by the various relevant specifications. - During runtime, and for local configuration and state, + During runtime, and for local configuration and runtime state, additional directories are defined: diff --git a/man/homectl.xml b/man/homectl.xml index 23348f00b..0724749c0 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -50,7 +50,7 @@ An individual LUKS2 encrypted loopback file for a user, stored in /home/*.home. At login the file system contained in this files is mounted, after the LUKS2 encrypted volume has been attached. The user's password is identical to the encryption - passphrase of the LUKS2 volume. Access to data without preceeding user authentication is thus not + passphrase of the LUKS2 volume. Access to data without preceding user authentication is thus not possible, even for the system administrator. This storage mechanism provides the strongest data security and is thus recommended. @@ -119,9 +119,9 @@ FILERead the user's JSON record from the specified file. If passed as - - reads the user record from standard input. The supplied JSON object must follow - the structure documented on JSON User - Records. This option may be used in conjunction with the create and + - read the user record from standard input. The supplied JSON object must follow + the structure documented on JSON User Records. + This option may be used in conjunction with the create and update commands (see below), where it allows configuring the user record in JSON as-is, instead of setting the individual user record properties (see below). @@ -247,10 +247,9 @@ different system and the configured UID is taken by another user there, then systemd-homed may assign the user a different UID on that system. The specified UID must be outside of the system user range. It is recommended to use the 60001…60513 UID range for - this purpose. If not specified the UID is automatically picked. When logging in and the home - directory is found to be owned by a UID not matching the user's assigned one the home directory and - all files and directories inside it will have their ownership changed automatically before login - completes. + this purpose. If not specified, the UID is automatically picked. If the home directory is found to be + owned by a different UID when logging in, the home directory and everything underneath it will have + its ownership changed automatically before login completes.Note that users managed by systemd-homed always have a matching group associated with the same name as well as a GID matching the UID of the user. Thus, configuring the @@ -266,18 +265,19 @@ privileges. Note that systemd-homed does not manage any groups besides a group matching the user in name and numeric UID/GID. Thus any groups listed here must be registered independently, for example with groupadd8. If - non-existant groups that are listed there are ignored. This option may be used more than once, in - which case all specified group lists are combined. + project='man-pages'>groupadd8. + Any non-existent groups are ignored. This option may be used more than once, in which case all + specified group lists are combined. If the user is currently a member of a group which is not listed, + the user will be removed from the group. PATH Takes a file system path to a directory. Specifies the skeleton directory to - initialize the home directory with. All files and directories in the specified are copied into any - newly create home directory. If not specified defaults to - /etc/skel/. + initialize the home directory with. All files and directories in the specified path are copied into + any newly create home directory. If not specified defaults to /etc/skel/. + @@ -312,7 +312,7 @@ Takes a specifier indicating the preferred language of the user. The $LANG environment variable is initialized from this value on login, and thus a value suitable for this environment variable is accepted here, for example - + . @@ -330,8 +330,50 @@ security token with exactly one pair of X.509 certificate and private key. A random secret key is then generated, encrypted with the public key of the X.509 certificate, and stored as part of the user record. At login time it is decrypted with the PKCS#11 module and then used to unlock the - account and associated resources. See below for an example how to set up authentication with security - token. + account and associated resources. See below for an example how to set up authentication with a + security token. + + Instead of a valid PKCS#11 URI, the special strings list and + auto may be specified. If list is passed, a brief table of + suitable, currently plugged in PKCS#11 hardware tokens is shown, along with their URIs. If + auto is passed, a suitable PKCS#11 hardware token is automatically selected (this + operation will fail if there isn't exactly one suitable token discovered). The latter is a useful + shortcut for the most common case where a single PKCS#11 hardware token is plugged in. + + Note that many hardware security tokens implement both PKCS#11/PIV and FIDO2 with the + hmac-secret extension (for example: the YubiKey 5 series), as supported with the + option below. Both mechanisms are similarly powerful, though FIDO2 + is the more modern technology. PKCS#11/PIV tokens have the benefit of being recognizable before + authentication and hence can be used for implying the user identity to use for logging in, which + FIDO2 does not allow. PKCS#11/PIV devices generally require initialization (i.e. storing a + private/public key pair on them, see example below) before they can be used; FIDO2 security tokens + generally do not required that, and work out of the box. + + + + PATH + + Takes a path to a Linux hidraw device + (e.g. /dev/hidraw1), referring to a FIDO2 security token implementing the + hmac-secret extension, that shall be able to unlock the user account. If used, a + random salt value is generated on the host, which is passed to the FIDO2 device, which calculates a + HMAC hash of it, keyed by its internal secret key. The result is then used as key for unlocking the + user account. The random salt is included in the user record, so that whenever authentication is + needed it can be passed again to the FIDO2 token, to retrieve the actual key. + + Instead of a valid path to a FIDO2 hidraw device the special strings + list and auto may be specified. If list is + passed, a brief table of suitable discovered FIDO2 devices is shown. If auto is + passed, a suitable FIDO2 token is automatically selected, if exactly one is discovered. The latter is + a useful shortcut for the most common case where a single FIDO2 hardware token is plugged in. + + Note that FIDO2 devices suitable for this option must implement the + hmac-secret extension. Most current devices (such as the YubiKey 5 series) do. If + the extension is not implemented the device cannot be used for unlocking home directories. + + Note that many hardware security tokens implement both FIDO2 and PKCS#11/PIV (and thus may be + used with either or ), for a + discussion see above. @@ -395,20 +437,20 @@ Each of these options takes a time span specification as argument (in the syntax documented in - systemd.time5) and - configure various aspects of the user's password expiration policy. Specifically, + systemd.time7) and + configures various aspects of the user's password expiration policy. Specifically, configures how much time has to pass after changing the password of the user until the password may be changed again. If the user tries to change their password before this time passes the attempt is refused. - configures how much time has to pass after the the password is changed until the password expires and - needs to be changed again. After this time passes any attempts to log in may only proceed after the - password is changed. specifies how much earlier than then - the time configured with the user is warned at login to - change their password as it will expire soon. Finally - configures the time which has to pass after the password as expired until the user is not permitted - to log in or change the password anymore. Note that these options only apply to password - authentication, and do not apply to other forms of authentication, for example PKCS#11-based security - token authentication. + configures how soon after it has been changed the password expires and needs to be changed again. + After this time passes logging in may only proceed after the password is changed. + specifies how much earlier than then the time configured + with the user is warned at login to change their password as + it will expire soon. Finally configures the time which + has to pass after the password as expired until the user is not permitted to log in or change the + password anymore. Note that these options only apply to password authentication, and do not apply to + other forms of authentication, for example PKCS#11-based security token + authentication. @@ -467,7 +509,7 @@ su1 or a similar tool. Use to place a limit on the tasks actually running under the UID of the user, thus excluding any child processes that might have changed user - identity. This controls the TasksMax= settting of the per-user systemd slice unit + identity. This controls the TasksMax= setting of the per-user systemd slice unit user-$UID.slice. See systemd.resource-control5 for further details. @@ -490,7 +532,7 @@ WEIGHT WEIGHT - Set a CPU and IO scheduling weights of the processes of the user, including those of + Set CPU and IO scheduling weights of the processes of the user, including those of processes forked off by the user that changed user credentials. Takes a numeric value in the range 1…10000. This controls the CPUWeight= and IOWeight= settings of the per-user systemd slice unit user-$UID.slice. See @@ -504,9 +546,9 @@ Selects the storage mechanism to use for this home directory. Takes one of luks, fscrypt, directory, subvolume, cifs. For details about these mechanisms, see - above. If a new home directory is created and the storage type is not specifically specified defaults - to luks if supported, subvolume as first fallback if supported, - and directory if not. + above. If a new home directory is created and the storage type is not specifically specified, + homed.conf5 + defines which default storage to use. @@ -525,9 +567,10 @@ When LUKS2 storage is used configures the file system type to use inside the home directory LUKS2 container. One of ext4, xfs, - btrfs. If not specified defaults to ext4. Note that - xfs is not recommended as its support for file system resizing is too - limited. + btrfs. If not specified + homed.conf5 + defines which default file system type to use. Note that xfs is not recommended as + its support for file system resizing is too limited. @@ -544,6 +587,16 @@ loopback file) the discard logic defaults to on. + + BOOL + + Similar to , controls the trimming of the file + system. However, while controls what happens when the home directory + is active, controls what happens when it becomes inactive, + i.e. whether to trim/allocate the storage when deactivating the home directory. This option defaults + to on, to ensure disk space is minimized while a user is not logged in. + + CIPHER MODE @@ -641,8 +694,8 @@ Activation of a home directory involves various operations that depend on the selected storage mechanism. If the LUKS2 mechanism is used, this generally involves: inquiring the user for a password, setting up a loopback device, validating and activating the LUKS2 volume, checking the file - system, mounting the file system, and potentiatlly changing the ownership of all included files to - the correct UID/GID. + system, mounting the file system, and potentially changing the ownership of all included files to the + correct UID/GID. @@ -707,7 +760,7 @@ passwd USER - Change the password of the specified home direcory/user account. + Change the password of the specified home directory/user account. @@ -798,7 +851,7 @@ - Set up authentication with a YubiKey security token: + Set up authentication with a YubiKey security token using PKCS#11/PIV: # Clear the Yubikey from any old keys (careful!) ykman piv reset @@ -809,16 +862,18 @@ ykman piv generate-key -a RSA2048 9d pubkey.pem # Create a self-signed certificate from this public key, and store it on the device. ykman piv generate-certificate --subject "Knobelei" 9d pubkey.pem -# We don't need the publibc key on disk anymore +# We don't need the public key on disk anymore rm pubkey.pem -# Check if the newly create key on the Yubikey shows up as token in PKCS#11. Have a look at the output, and -# copy the resulting token URI to the clipboard. -p11tool --list-tokens +# Allow the security token to unlock the account of user 'lafcadio'. +homectl update lafcadio --pkcs11-token-uri=auto + -# Allow the security token referenced by the determined PKCS#11 URI to unlock the account of user -# 'lafcadio'. (Replace the '…' by the URI from the clipboard.) -homectl update lafcadio --pkcs11-token-uri=… + + Set up authentication with a FIDO2 security token: + + # Allow a FIDO2 security token to unlock the account of user 'nihilbaxter'. +homectl update nihilbaxter --fido2-device=auto @@ -827,6 +882,7 @@ homectl update lafcadio --pkcs11-token-uri=… systemd1, systemd-homed.service8, + homed.conf5, userdbctl1, useradd8, cryptsetup8 diff --git a/man/homed.conf.xml b/man/homed.conf.xml new file mode 100644 index 000000000..2c5f3355d --- /dev/null +++ b/man/homed.conf.xml @@ -0,0 +1,84 @@ + + + + + + + homed.conf + systemd + + + + homed.conf + 5 + + + + homed.conf + homed.conf.d + Home area/user account manager configuration files + + + + /etc/systemd/homed.conf + /etc/systemd/homed.conf.d/*.conf + /run/systemd/homed.conf.d/*.conf + /usr/lib/systemd/homed.conf.d/*.conf + + + + Description + + These configuration files control default parameters for home areas/user accounts created and + managed by + systemd-homed.service8. + + + + + + + Options + + The following options are available in the [Home] section: + + + + + DefaultStorage= + The default storage to use for home areas. Takes one of luks, + fscrypt, directory, subvolume, + cifs. For details about these options, see + homectl1. If not + configured or assigned the empty string, the default storage is automatically determined: if not + running in a container environment and /home/ is not itself encrypted, defaults + to luks. Otherwise defaults to subvolume if + /home/ is on a btrfs file system, and directory + otherwise. Note that the storage selected on the homectl command line always takes + precedence. + + + + DefaultFileSystemType= + When using luks as storage (see above), selects the default file + system to use inside the user's LUKS volume. Takes one of ext4, + xfs or btrfs. If not specified defaults to + ext4. This setting has no effect if a different storage mechanism is used. The + file system type selected on the homectl command line always takes + precedence. + + + + + + + See Also + + systemd1, + systemd-homed.service8 + + + + diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml index 03293382e..7ca62f481 100644 --- a/man/hostnamectl.xml +++ b/man/hostnamectl.xml @@ -57,7 +57,7 @@ Use systemd-firstboot1 - to initialize the system host name for mounted (but not booted) + to initialize the system hostname for mounted (but not booted) system images. @@ -84,7 +84,7 @@ simplified in regards to the character set used before the latter are updated. This is done by removing special characters and spaces. This ensures that the pretty and the static hostname are always closely related while still following the validity rules of the specific name. This simplification of the hostname string is not done - if only the transient and/or static host names are set, and the pretty host name is left untouched. + if only the transient and/or static hostnames are set, and the pretty hostname is left untouched. Pass the empty string as the hostname to reset the selected hostnames to their default diff --git a/man/html.in b/man/html.in index bc9a668c2..b7e79841d 100755 --- a/man/html.in +++ b/man/html.in @@ -6,6 +6,9 @@ if [ -z "$1" ]; then exit 1 fi +# make sure the rules have been regenerated (in case man/update-man-rules was just run) +ninja -C "@BUILD_ROOT@" version.h + target="man/$1.html" ninja -C "@BUILD_ROOT@" "$target" set -x diff --git a/man/hwdb-usb-device.c b/man/hwdb-usb-device.c new file mode 100644 index 000000000..8a4b86e7b --- /dev/null +++ b/man/hwdb-usb-device.c @@ -0,0 +1,28 @@ +#include +#include +#include + +int print_usb_properties(uint16_t vid, uint16_t pid) { + char match[15]; + sd_hwdb *hwdb; + const char *key, *value; + int r; + + /* Match this USB vendor and product ID combination */ + snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid); + + r = sd_hwdb_new(&hwdb); + if (r < 0) + return r; + + SD_HWDB_FOREACH_PROPERTY(hwdb, match, key, value) + printf("%s: \"%s\" → \"%s\"\n", match, key, value); + + sd_hwdb_unref(hwdb); + return 0; +} + +int main(int argc, char **argv) { + print_usb_properties(0x046D, 0xC534); + return 0; +} diff --git a/man/journal-remote.conf.xml b/man/journal-remote.conf.xml index ab3dfd011..f6a9f1565 100644 --- a/man/journal-remote.conf.xml +++ b/man/journal-remote.conf.xml @@ -39,7 +39,7 @@ These files configure various parameters of systemd-journal-remote.service8. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -49,7 +49,7 @@ Options All options are configured in the - [Remote] section: + [Remote] section: diff --git a/man/journal-upload.conf.xml b/man/journal-upload.conf.xml index f2721a75e..2a67e6fd0 100644 --- a/man/journal-upload.conf.xml +++ b/man/journal-upload.conf.xml @@ -34,7 +34,7 @@ These files configure various parameters of systemd-journal-upload.service8. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -43,7 +43,7 @@ Options - All options are configured in the [Upload] section: + All options are configured in the [Upload] section: diff --git a/man/journalctl.xml b/man/journalctl.xml index 1e5f95e19..07310d90a 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -374,9 +374,10 @@ - generates a very terse output, only showing the - actual message of each journal entry with no metadata, - not even a timestamp. + generates a very terse output, only showing the actual message of each journal entry + with no metadata, not even a timestamp. If combined with the + option will output the listed fields for each log record, + instead of the message. @@ -398,10 +399,11 @@ - A comma separated list of the fields which should be included in the output. This has an - effect only for the output modes which would normally show all fields (, - , , , and - ). The __CURSOR, __REALTIME_TIMESTAMP, + A comma separated list of the fields which should be included in the output. This has + an effect only for the output modes which would normally show all fields (, + , , , + and ), as well as on . For the + former, the __CURSOR, __REALTIME_TIMESTAMP, __MONOTONIC_TIMESTAMP, and _BOOT_ID fields are always printed. @@ -553,7 +555,7 @@ is also added for _SYSTEMD_SLICE=UNIT, such that if the provided UNIT is a systemd.slice5 - unit, all logs of the children of the slice will be logged. + unit, all logs of children of the slice will be shown. This parameter can be specified multiple times. @@ -572,7 +574,7 @@ is also added for _SYSTEMD_USER_SLICE=UNIT, such that if the provided UNIT is a systemd.slice5 - unit, all logs of the children of the unit will be logged. + unit, all logs of children of the unit will be shown. This parameter can be specified multiple times. @@ -619,7 +621,7 @@ Filter output to entries where the MESSAGE= field matches the specified regular expression. PERL-compatible regular expressions are used, see - pcre2pattern3 + pcre2pattern3 for a detailed description of the syntax. If the pattern is all lowercase, matching is case insensitive. @@ -631,7 +633,7 @@ - Make pattern matching case sensitive or case insenstive. + Make pattern matching case sensitive or case insensitive. @@ -759,8 +761,8 @@ underneath the specified directory instead of the root directory (e.g. will create ROOT/var/lib/systemd/catalog/database, - and journal files under ROOT/run/journal - or ROOT/var/log/journal will be displayed). + and journal files under ROOT/run/journal/ + or ROOT/var/log/journal/ will be displayed). @@ -927,10 +929,10 @@ /run/log/journal/ into /var/log/journal/, if persistent storage is enabled. This call does not return until the operation is complete. Note that this call is idempotent: the data is only flushed from /run/log/journal/ into - /var/log/journal once during system runtime (but see + /var/log/journal/ once during system runtime (but see below), and this command exits cleanly without executing any operation if this has already happened. This command effectively guarantees that all data is flushed - to /var/log/journal at the time it returns. + to /var/log/journal/ at the time it returns. @@ -1020,7 +1022,7 @@ journalctl _SYSTEMD_CGROUP=/user.slice/user-42.slice/session-c1.scopename.service _UID=0 + COREDUMP_UNIT=name.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1 - (see systemd.journal-fields5 + (see systemd.journal-fields7 for an explanation of those patterns). diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 957ec36a6..bfd359a90 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -36,7 +36,7 @@ These files configure various parameters of the systemd journal service, systemd-journald.service8. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. The systemd-journald instance managing the default namespace is configured by @@ -53,7 +53,7 @@ Options All options are configured in the - [Journal] section: + [Journal] section: diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 4aac14ea1..b67639c92 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -109,7 +109,7 @@ During early boot, the generation of core dump files is disabled until a core dump handler (if any) takes over. This parameter allows specifying an absolute path where core dump files should be stored until a handler is installed. The path should be absolute and may contain specifiers, see - core5 for details. + core5 for details. @@ -271,6 +271,10 @@ rd.udev.exec_delay= udev.event_timeout= rd.udev.event_timeout= + udev.timeout_signal= + rd.udev.timeout_signal= + udev.blockdev_read_only + rd.udev.blockdev_read_only net.ifnames= net.naming-scheme= @@ -433,8 +437,71 @@ Takes a boolean argument, defaults to on. If off, systemd-firstboot.service8 - will not query the user for basic system settings, even if the system boots up for the first time and the - relevant settings are not initialized yet. + will not query the user for basic system settings, even if the system boots up for the first time and + the relevant settings are not initialized yet. Not to be confused with + systemd.condition-first-boot= (see below), which overrides the result of the + ConditionFirstBoot= unit file condition, and thus controls more than just + systemd-firstboot.service behaviour. + + + + systemd.condition-needs-update= + + Takes a boolean argument. If specified, overrides the result of + ConditionNeedsUpdate= unit condition checks. See + systemd.unit5 for + details. + + + + systemd.condition-first-boot= + + Takes a boolean argument. If specified, overrides the result of + ConditionFirstBoot= unit condition checks. See + systemd.unit5 for + details. Not to be confused with systemd.firstboot= which only controls behaviour + of the systemd-firstboot.service system service but has no effect on the + condition check (see above). + + + + systemd.clock-usec= + + Takes a decimal, numeric timestamp in µs since January 1st 1970, 00:00am, to set the + system clock to. The system time is set to the specified timestamp early during boot. It is not + propagated to the hardware clock (RTC). + + + + systemd.random-seed= + + Takes a base64 encoded random seed value to credit with full entropy to the kernel's + random pool during early service manager initialization. This option is useful in testing + environments where delays due to random pool initialization in entropy starved virtual machines shall + be avoided. + + Note that if this option is used the seed is accessible to unprivileged programs from + /proc/cmdline. This option is hence a security risk when used outside of test + systems, since the (possibly) only seed used for initialization of the kernel's entropy pool might be + easily acquired by unprivileged programs. + + It is recommended to pass 512 bytes of randomized data (as that matches the Linux kernel pool + size), which may be generated with a command like the following: + + dd if=/dev/urandom bs=512 count=1 status=none | base64 -w 0 + + Again: do not use this option outside of testing environments, it's a security risk elsewhere, + as secret key material derived from the entropy pool can possibly be reconstructed by unprivileged + programs. + + + + + systemd.hostname= + + Accepts a hostname to set during early boot. If specified takes precedence over what + is set in /etc/hostname. Note that this does not bar later runtime changes to + the hostname, it simply controls the initial hostname set during early boot. diff --git a/man/kernel-install.xml b/man/kernel-install.xml index 34bb5d2f1..273270f55 100644 --- a/man/kernel-install.xml +++ b/man/kernel-install.xml @@ -36,7 +36,7 @@ Descriptionkernel-install is used to install and remove kernel and initramfs images to and from the boot loader partition, referred to as $BOOT here. It will usually be one of - /boot, /efi, or /boot/efi, see below. + /boot/, /efi/, or /boot/efi/, see below. kernel-install will execute the files @@ -80,7 +80,7 @@ 50-depmod.install runs - depmod8 for the + depmod8 for the KERNEL-VERSION.90-loaderentry.install copies KERNEL-IMAGE @@ -137,7 +137,7 @@ 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 + /efi/, /boot/, and /boot/efi/ in turn. The first location where $BOOT/loader/entries/ or $BOOT/$MACHINE_ID/ exists is used. @@ -231,7 +231,7 @@ machine-id5, os-release5, - depmod8, + depmod8, systemd-boot7, Boot Loader Specification diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 14f84c13e..4b01a93bd 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -165,7 +165,7 @@ variable is set, and then derive the random seed to pass to the OS from the combination. If always the boot loader will do so even if LoaderSystemToken is not set. This mode is useful in environments where protection against OS image reuse is not a - concern, and the random seed shall be used even with no further setup in place. User bootctl + concern, and the random seed shall be used even with no further setup in place. Use bootctl random-seed to initialize both the random seed file in the ESP and the system token EFI variable. diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 4cbfd09cb..81b870c46 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -36,7 +36,7 @@ These files configure various parameters of the systemd login manager, systemd-logind.service8. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -46,7 +46,7 @@ Options All options are configured in the - [Login] section: + [Login] section: @@ -277,7 +277,7 @@ HoldoffTimeoutSec= - Specifies the timeout after system startup or + Specifies a period of time after system startup or system resume in which systemd will hold off on reacting to lid events. This is required for the system to properly detect any hotplugged devices so systemd can ignore lid events @@ -304,6 +304,19 @@ memory as is needed. + + RuntimeDirectoryInodesMax= + + Sets the limit on number of inodes for the + $XDG_RUNTIME_DIR runtime directory for each + user who logs in. Takes a number, optionally suffixed with the + usual K, G, M, and T suffixes, to the base 1024 (IEC). + Defaults to RuntimeDirectorySize= divided + by 4096. Note that this size is a safety limit only. + As each runtime directory is a tmpfs file system, it will + only consume as much memory as is needed. + + InhibitorsMax= diff --git a/man/machine-id.xml b/man/machine-id.xml index ebee065a6..bd55366ac 100644 --- a/man/machine-id.xml +++ b/man/machine-id.xml @@ -39,7 +39,7 @@ The machine ID may be set, for example when network booting, with the systemd.machine_id= kernel command line parameter or by passing the - option to systemd. An ID is specified in this manner + option to systemd. An ID specified in this manner has higher priority and will be used instead of the ID stored in /etc/machine-id. diff --git a/man/machine-info.xml b/man/machine-info.xml index 4fb27abe0..7a0a396a1 100644 --- a/man/machine-info.xml +++ b/man/machine-info.xml @@ -70,7 +70,7 @@ Lennart's Computer an Internet hostname of lennarts-computer might be a good choice. If this parameter is not set, an application should fall back - to the Internet host name for presentation + to the Internet hostname for presentation purposes. diff --git a/man/machinectl.xml b/man/machinectl.xml index c211ca02a..37e51f90c 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -56,7 +56,7 @@ Machines are identified by names that follow the same rules - as UNIX and DNS host names. For details, see below. + as UNIX and DNS hostnames. For details, see below.Machines are instantiated from disk or file system images that frequently — but not necessarily — carry the same name as machines running @@ -320,7 +320,7 @@ Copies files or directories from a container into the host system. Takes a container name, followed by the - source path in the container the destination path on the host. + source path in the container and the destination path on the host. If the destination path is omitted, the same as the source path is used. @@ -383,7 +383,7 @@ image is optimized for file systems that support copy-on-write, and might not be efficient on others, due to file system limitations. - Note that this command leaves host name, machine ID and + Note that this command leaves hostname, machine ID and all other settings that could identify the instance unmodified. The original image and the cloned copy will hence share these credentials, and it might be necessary to manually @@ -851,7 +851,7 @@ The machinectl tool operates on machines and images whose names must be chosen following strict - rules. Machine names must be suitable for use as host names + rules. Machine names must be suitable for use as hostnames following a conservative subset of DNS and UNIX/Linux semantics. Specifically, they must consist of one or more non-empty label strings, separated by dots. No leading or trailing diff --git a/man/man.in b/man/man.in index 75680b860..fa6164b8d 100755 --- a/man/man.in +++ b/man/man.in @@ -6,6 +6,9 @@ if [ -z "$1" ]; then exit 1 fi +# make sure the rules have been regenerated (in case man/update-man-rules was just run) +ninja -C "@BUILD_ROOT@" version.h + page="$(echo "$1" | sed 's/\./\\./')" target=$(ninja -C "@BUILD_ROOT@" -t query man/man | grep -E -m1 "man/$page\.[0-9]$" | awk '{print $2}') if [ -z "$target" ]; then diff --git a/man/meson.build b/man/meson.build index 8021adede..3a7143a4b 100644 --- a/man/meson.build +++ b/man/meson.build @@ -34,6 +34,7 @@ custom_entities_ent = configure_file( man_pages = [] html_pages = [] source_xml_files = [] +dbus_docs = [] foreach tuple : xsltproc.found() ? manpages : [] stem = tuple[0] section = tuple[1] @@ -90,7 +91,11 @@ foreach tuple : xsltproc.found() ? manpages : [] install_dir : join_paths(docdir, 'html')) html_pages += p3 - source_xml_files += files(tuple[0] + '.xml') + file = files(tuple[0] + '.xml') + source_xml_files += file + if tuple[0].startswith('org.freedesktop.') + dbus_docs += file + endif else message('Skipping @0@.@1@ because @2@ is false'.format(stem, section, condition)) endif @@ -105,9 +110,9 @@ endif systemd_directives_xml = custom_target( 'systemd.directives.xml', - input : source_xml_files, + input : ['directives-template.xml', source_xml_files], output : 'systemd.directives.xml', - command : [make_directive_index_py, '@OUTPUT@'] + source_xml_files) + command : [make_directive_index_py, '@OUTPUT@', '@INPUT@']) nonindex_xml_files = source_xml_files + [systemd_directives_xml] systemd_index_xml = custom_target( @@ -193,13 +198,26 @@ run_target( ############################################################ +if dbus_docs.length() > 0 + custom_target( + 'update-dbus-docs', + output : 'update-dbus-docs', + command : ['python3', + '@0@/tools/update-dbus-docs.py'.format(project_source_root), + '--build-dir=@0@'.format(project_build_root), + '@INPUT@'], + input : dbus_docs) +endif + +############################################################ + if git.found() custom_target( 'update-man-rules', output : 'update-man-rules', command : ['sh', '-c', 'cd @0@ && '.format(meson.build_root()) + - 'python3 @0@/tools/make-man-rules.py $(git ls-files ":/man/*.xml") >t && '.format(project_source_root) + + 'python3 @0@/tools/update-man-rules.py $(git ls-files ":/man/*.xml") >t && '.format(project_source_root) + 'mv t @0@/rules/meson.build'.format(meson.current_source_dir())], depend_files : custom_entities_ent) endif diff --git a/man/networkctl.xml b/man/networkctl.xml index 52876f377..bd958fb1e 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -233,7 +233,7 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) Show numerical address labels that can be used for address selection. This is the same information that - ip-addrlabel8 + ip-addrlabel8 shows. See RFC 3484 for a discussion of address labels. @@ -259,6 +259,20 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) Deletes virtual netdevs. Takes interface name or index number. + + + up + + Bring devices up. Takes interface name or index number. + + + + + down + + Bring devices down. Takes interface name or index number. + + renew @@ -267,6 +281,14 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR) Takes interface name or index number. + + + forcerenew + + Send a FORCERENEW message to all connected clients, triggering DHCP reconfiguration. + Takes interface name or index number. + + reconfigure diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml index 2041ddd42..e8e41ebe9 100644 --- a/man/networkd.conf.xml +++ b/man/networkd.conf.xml @@ -45,7 +45,7 @@ [Network] Section Options - The following options are available in the [Network] section: + The following options are available in the [Network] section: @@ -61,6 +61,15 @@ Specifies the time interval to calculate the traffic speed of each interface. If SpeedMeter=no, the value is ignored. Defaults to 10sec. + + + ManageForeignRoutes= + A boolean. When true, systemd-networkd will store any routes + configured by other tools in its memory. When false, systemd-networkd will + not manage the foreign routes, thus they are kept even if KeepConfiguration= + is false. Defaults to yes. + + diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml index 908c91eb7..a41c383bb 100644 --- a/man/nss-myhostname.xml +++ b/man/nss-myhostname.xml @@ -18,8 +18,7 @@ nss-myhostname libnss_myhostname.so.2 - Provide hostname resolution for the locally - configured system hostname. + Hostname resolution for the locally configured system hostname @@ -67,9 +66,13 @@ To activate the NSS modules, add myhostname to the line starting with hosts: in /etc/nsswitch.conf. - It is recommended to place myhostname last in the nsswitch.conf' - hosts: line to make sure that this mapping is only used as fallback, and that any DNS or - /etc/hosts based mapping takes precedence. + It is recommended to place myhostname either between resolve + and "traditional" modules like files and dns, or after them. In the + first version, well-known names like localhost and the machine hostname are given + higher priority than the external configuration. This is recommended when the external DNS servers and + network are not absolutely trusted. In the second version, external configuration is given higher + priority and nss-myhostname only provides a fallback mechanism. This might be suitable + in closely controlled networks, for example on a company LAN. @@ -79,11 +82,14 @@ nss-myhostname correctly: -passwd: compat mymachines systemd -group: compat mymachines systemd +passwd: compat systemd +group: compat systemd shadow: compat -hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname +# Either (untrusted network): +hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns +# Or (only trusted networks): +hosts: mymachines resolve [!UNAVAIL=return] files dns myhostname networks: files protocols: db files diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml index 40b0abee3..e0e6989c4 100644 --- a/man/nss-mymachines.xml +++ b/man/nss-mymachines.xml @@ -18,8 +18,7 @@ nss-mymachines libnss_mymachines.so.2 - Provide hostname resolution for local - container instances. + Hostname resolution for local container instances @@ -40,22 +39,13 @@ Note that the name that is resolved is the one registered with systemd-machined, which may be different than the hostname configured inside of the container. - The module also provides name resolution for user and group identifiers mapped to containers. All names from - the range allocated to a given container container are exposed on the host as - vu-container-uid and - vg-container-gid (see example below). This - functionality only applies to containers using user namespacing (see the description of - in - systemd-nspawn1). - - To activate the NSS module, add mymachines to the lines starting with - hosts:, passwd: and group: in - /etc/nsswitch.conf. + To activate the NSS module, add mymachines to the line starting with + hosts: in /etc/nsswitch.conf.It is recommended to place mymachines after the files or - compat entry of the /etc/nsswitch.conf lines to make sure that its mappings - are preferred over other resolvers such as DNS, but so that /etc/hosts, - /etc/passwd and /etc/group based mappings take precedence. + compat entry of the /etc/nsswitch.conf line to make sure that its + mappings are preferred over other resolvers such as DNS, but so that /etc/hosts + based mappings take precedence. @@ -65,11 +55,11 @@ nss-mymachines correctly: - passwd: compat mymachines systemd -group: compat mymachines systemd + passwd: compat systemd +group: compat systemd shadow: compat -hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname +hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns networks: files protocols: db files @@ -82,7 +72,7 @@ netgroup: nis - Mappings provided by <filename>nss-mymachines</filename> + Example: Mappings provided by <filename>nss-mymachines</filename> The container rawhide is spawned using systemd-nspawn1: @@ -97,29 +87,6 @@ $ machinectl --max-addresses=3 MACHINE CLASS SERVICE OS VERSION ADDRESSES rawhide container systemd-nspawn fedora 30 169.254.40.164 fe80::94aa:3aff:fe7b:d4b9 -$ getent passwd vu-rawhide-0 vu-rawhide-81 -vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin -vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin - -$ getent group vg-rawhide-0 vg-rawhide-81 -vg-rawhide-0:*:20119552: -vg-rawhide-81:*:20119633: - -$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide' -vu-rawhide-0 692 ? /usr/lib/systemd/systemd -vu-rawhide-0 731 ? /usr/lib/systemd/systemd-journald -vu-rawhide-192 734 ? /usr/lib/systemd/systemd-networkd -vu-rawhide-193 738 ? /usr/lib/systemd/systemd-resolved -vu-rawhide-0 742 ? /usr/lib/systemd/systemd-logind -vu-rawhide-81 744 ? /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only -vu-rawhide-0 746 ? /usr/sbin/sshd -D ... -vu-rawhide-0 752 ? /usr/lib/systemd/systemd --user -vu-rawhide-0 753 ? (sd-pam) -vu-rawhide-0 1628 ? login -- zbyszek -vu-rawhide-1000 1630 ? /usr/lib/systemd/systemd --user -vu-rawhide-1000 1631 ? (sd-pam) -vu-rawhide-1000 1637 pts/8 -zsh - $ ping -c1 rawhide PING rawhide(fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide)) 56 data bytes 64 bytes from fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide): icmp_seq=1 ttl=64 time=0.045 ms diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index e4ea4e189..c37746895 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -18,7 +18,7 @@ nss-resolve libnss_resolve.so.2 - Provide hostname resolution via systemd-resolved.service + Hostname resolution via systemd-resolved.service @@ -29,19 +29,20 @@ Description nss-resolve is a plug-in module for the GNU Name Service Switch (NSS) functionality of the - GNU C Library (glibc) enabling it to resolve host names via the + GNU C Library (glibc) enabling it to resolve hostnames via the systemd-resolved8 local network name resolution service. It replaces the nss-dns plug-in module that traditionally resolves hostnames via DNS. - To activate the NSS module, add resolve to the line starting with - hosts: in /etc/nsswitch.conf. Specifically, it is recommended to place - resolve early in /etc/nsswitch.conf's hosts: line (but - after the files or mymachines entries), right before the - dns entry if it exists, followed by [!UNAVAIL=return], to ensure DNS queries - are always routed via - systemd-resolved8 if it is - running, but are routed to nss-dns if this service is not available. + To activate the NSS module, add resolve [!UNAVAIL=return] to the line starting + with hosts: in /etc/nsswitch.conf. Specifically, it is + recommended to place resolve early in /etc/nsswitch.conf's + hosts: line. It should be before the files entry, since + systemd-resolved supports /etc/hosts internally, but with + caching. To the contrary, it should be after mymachines, to give hostnames given to + local VMs and containers precedence over names received over DNS. Finally, we recommend placing + dns somewhere after resolve, to fall back to + nss-dns if systemd-resolved.service is not available. Note that systemd-resolved will synthesize DNS resource records in a few cases, for example for localhost and the @@ -62,11 +63,11 @@ correctly: -passwd: compat mymachines systemd -group: compat mymachines systemd +passwd: compat systemd +group: compat systemd shadow: compat -hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname +hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns networks: files protocols: db files diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml index e343c406f..34aee0e88 100644 --- a/man/nss-systemd.xml +++ b/man/nss-systemd.xml @@ -18,7 +18,7 @@ nss-systemd libnss_systemd.so.2 - Provide UNIX user and group name resolution for user/group lookup via Varlink + UNIX user and group name resolution for user/group lookup via Varlink @@ -35,8 +35,8 @@ systemd1 (for its DynamicUser= feature, see systemd.exec5 for - details) or - systemd-homed.service8. + details), + systemd-homed.service8, or systemd-machined.service8. This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs 0 and 65534) remain resolvable at all times, even if they aren't listed in /etc/passwd or @@ -55,17 +55,17 @@ - Example + Configuration in <filename>/etc/nsswitch.conf</filename> Here is an example /etc/nsswitch.conf file that enables nss-systemd correctly: - passwd: compat mymachines systemd -group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd + passwd: compat systemd +group: compat [SUCCESS=merge] systemd shadow: compat -hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname +hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns networks: files protocols: db files @@ -77,6 +77,47 @@ netgroup: nis + + Example: Mappings provided by <filename>systemd-machined.service</filename> + + The container rawhide is spawned using + systemd-nspawn1: + + + # systemd-nspawn -M rawhide --boot --network-veth --private-users=pick +Spawning container rawhide on /var/lib/machines/rawhide. +Selected user namespace base 20119552 and range 65536. +... + +$ machinectl --max-addresses=3 +MACHINE CLASS SERVICE OS VERSION ADDRESSES +rawhide container systemd-nspawn fedora 30 169.254.40.164 fe80::94aa:3aff:fe7b:d4b9 + +$ getent passwd vu-rawhide-0 vu-rawhide-81 +vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin +vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin + +$ getent group vg-rawhide-0 vg-rawhide-81 +vg-rawhide-0:*:20119552: +vg-rawhide-81:*:20119633: + +$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide' +vu-rawhide-0 692 ? /usr/lib/systemd/systemd +vu-rawhide-0 731 ? /usr/lib/systemd/systemd-journald +vu-rawhide-192 734 ? /usr/lib/systemd/systemd-networkd +vu-rawhide-193 738 ? /usr/lib/systemd/systemd-resolved +vu-rawhide-0 742 ? /usr/lib/systemd/systemd-logind +vu-rawhide-81 744 ? /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only +vu-rawhide-0 746 ? /usr/sbin/sshd -D ... +vu-rawhide-0 752 ? /usr/lib/systemd/systemd --user +vu-rawhide-0 753 ? (sd-pam) +vu-rawhide-0 1628 ? login -- zbyszek +vu-rawhide-1000 1630 ? /usr/lib/systemd/systemd --user +vu-rawhide-1000 1631 ? (sd-pam) +vu-rawhide-1000 1637 pts/8 -zsh + + + See Also @@ -85,6 +126,9 @@ netgroup: nis nss-resolve8, nss-myhostname8, nss-mymachines8, + systemd-userdbd.service8, + systemd-homed.service8, + systemd-machined.service8, nsswitch.conf5, getent1 diff --git a/man/org.freedesktop.LogControl1.xml b/man/org.freedesktop.LogControl1.xml new file mode 100644 index 000000000..d8ce392c8 --- /dev/null +++ b/man/org.freedesktop.LogControl1.xml @@ -0,0 +1,106 @@ + + + + + + + org.freedesktop.LogControl1 + systemd + + + + org.freedesktop.LogControl1 + 5 + + + + org.freedesktop.LogControl1 + D-Bus interface to query and set logging configuration + + + + Introduction + + org.freedesktop.LogControl1 is a generic interface that is intended + to be used by any daemon which should allow setting the log level and target over D-Bus. It is implemented + by various daemons that are part of the + systemd1 suite. + + It is assumed that those settings are global for the whole program, so a fixed object path is + used. The interface should always be available under the path + /org/freedesktop/LogControl1. + + + + Description + + The following interface is exposed: + + +node /org/freedesktop/LogControl1 { + interface org.freedesktop.LogControl1 { + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s LogLevel = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s LogTarget = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s SyslogIdentifier = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + Properties + + LogLevel describes the + syslog3-style + log-level, and should be one of emerg, alert, + crit, err, warning, notice, + info, debug, in order of increasing verbosity. + + LogTarget describes the log target (mechanism). It should be one of + console (log to the console or standard output), + kmsg (log to the kernel ring buffer), + journal (log to the journal natively, see + systemd-journald.service8), + syslog (log using the + syslog3 call). + + + Those two properties are writable, so they may be set by sufficiently privileged users. + + SyslogIdentifier is a read-only property that shows the "syslog identifier". + It is a short string that identifies the program that is the source of log messages that is passed to + the syslog3 call. + + + Note: journalctl option / may + be used to filter log messages by log level, option / + may be used to by the syslog identifier, and filters like _TRANSPORT=syslog, + _TRANSPORT=journal, and _TRANSPORT=kernel may be used to filter + messages by the mechanism through which they reached systemd-journald. + + + diff --git a/man/org.freedesktop.home1.xml b/man/org.freedesktop.home1.xml new file mode 100644 index 000000000..5cf115970 --- /dev/null +++ b/man/org.freedesktop.home1.xml @@ -0,0 +1,502 @@ + + + + + + + org.freedesktop.home1 + systemd + + + + org.freedesktop.home1 + 5 + + + + org.freedesktop.home1 + The D-Bus interface of systemd-homed + + + + Introduction + + systemd-homed.service8 + is a system service which may be used to create, remove, change or inspect home areas. This page + describes the D-Bus interface. + + + + + The Manager Object + + The service exposes the following interfaces on the Manager object on the bus: + + +node /org/freedesktop/home1 { + interface org.freedesktop.home1.Manager { + methods: + GetHomeByName(in s user_name, + out u uid, + out s home_state, + out u gid, + out s real_name, + out s home_directory, + out s shell, + out o bus_path); + GetHomeByUID(in u uid, + out s user_name, + out s home_state, + out u gid, + out s real_name, + out s home_directory, + out s shell, + out o bus_path); + GetUserRecordByName(in s user_name, + out s user_record, + out b incomplete, + out o bus_path); + GetUserRecordByUID(in u uid, + out s user_record, + out b incomplete, + out o bus_path); + ListHomes(out a(susussso) home_areas); + ActivateHome(in s user_name, + in s secret); + DeactivateHome(in s user_name); + RegisterHome(in s user_record); + UnregisterHome(in s user_name); + CreateHome(in s user_record); + RealizeHome(in s user_name, + in s secret); + RemoveHome(in s user_name); + FixateHome(in s user_name, + in s secret); + AuthenticateHome(in s user_name, + in s secret); + UpdateHome(in s user_record); + ResizeHome(in s user_name, + in t size, + in s secret); + ChangePasswordHome(in s user_name, + in s new_secret, + in s old_secret); + LockHome(in s user_name); + UnlockHome(in s user_name, + in s secret); + AcquireHome(in s user_name, + in s secret, + in b please_suspend, + out h send_fd); + RefHome(in s user_name, + in b please_suspend, + out h send_fd); + ReleaseHome(in s user_name); + LockAllHomes(); + properties: + readonly a(sso) AutoLogin = [...]; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + GetHomeByName() returns basic user information (a minimal subset of the full + user record), provided a user name. The information supplied more or less matches what + getpwnam3 returns: + the numeric UID and GID, the real name, home directory and shell. In addition it returns a state + identifier describing the state the user's home directory is in, as well as a bus path referring to the + bus object encapsulating the user record and home directory. This object implements the + org.freedesktop.home1.Home interface documented below. + + GetHomeByUID() is similar to GetHomeByName() but + acquires the information based on the numeric UID of the user. + + GetUserRecordByName() is also similar to + GetHomeByName() but returns the full JSON user record data instead of the broken + down records. An additional returned boolean indicates whether the record is complete or not. A record + is considered complete when its privileged section is included, and incomplete if it + was removed (see JSON User Records for details + about the various sections of a user record). Generally, only privileged clients and clients running + under the identity of the user itself get access to the privileged section and will + thus see complete records. + + GetUserRecordByUID() is similar to GetUserRecordByName() + but returns the user record matching the specified numeric UID. + + ListHomes() returns an array of all locally managed users. The array + contains the same fields GetHomeByName() returns: user name, numeric UID, state, + numeric GID, real name, home directory, shell and bus path of the matching bus object. + + ActivateHome() activates (i.e. mounts) the home directory of the specified + user. The second argument shall contain a user record consisting only of a secret + section (all other sections should be stripped, see JSON + User Records for details), and should contain only the secret credentials necessary for + unlocking the home directory. Typically a client would invoke this function first with an entirely + empty record (which is possibly sufficient if single-factor authentication with a plugged-in security + token is configured), and would then retry with a record populated with more information, depending on + the returned error code, in case more credentials are necessary. This function is synchronous and + returns only after the home directory was fully activated (or the operation failed), which might take + some time. Clients must be prepared for that, and typically should extend the D-Bus method call + timeout accordingly. This method is equivalent to the Activate() method on the + org.freedesktop.home1.Home interface documented below, but may be called on the + manager object and takes a user name as additional argument, instead. + + DeactivateHome() deactivates (i.e. unmounts) the home directory of the + specified user. It is equivalent to the Deactivate() method on the + org.freedesktop.home1.Home interface documented below. + + RegisterHome() registers a new home directory locally. It receives the JSON + user record as only argument (which typically excludes the secret + section). Registering a home directory just makes the user record known to the system, it does not + create a home directory or such (which is expected to exist already, or created later). This operation + is useful to register home directories locally that are not located where + systemd-homed.service would find them automatically. + + UnregisterHome() unregisters an existing home directory. It takes a user + name as argument and undoes what RegisterHome() does. It does not attempt to + remove the home directory itself, it just unregisters it with the local system. Note that if the home + directory is placed where systemd-homed.service looks for home directories anyway + this call will only undo fixation (see below), but the record will remain known to + systemd-homed.service and be listed among known records. Since the user record is + embedded into the home directory this operation generally does not discard data belonging to the user + or their record. This method is equivalent to + Unregister() on the org.freedesktop.home1.Home + interface. + + CreateHome() registers and creates a new home directory. This takes a fully + specified JSON user record as argument (including the secret section). This registers + the user record locally and creates a home directory matching it, depending on the settings specified + in the record in combination with local configuration. + + RealizeHome() creates a home directory whose user record is already + registered locally. This takes a user name plus a user record consisting only of the + secret section. Invoking RegisterHome() followed by + RealizeHome() is mostly equivalent to calling CreateHome(), + except that the latter combines the two in atomic fashion. This method is equivalent to + Realize() on the org.freedesktop.home1.Home + interface. + + RemoveHome() unregisters a user record locally, and removes the home + directory belonging to it, if it is accessible. It takes a user name as argument. This method is equivalent to + Remove() on the org.freedesktop.home1.Home + interface. + + FixateHome() fixates an automatically discovered home + directory. systemd-homed.service automatically discovers home directories dropped + in our plugged in and adds them to the runtime list of user records it manages. A user record + discovered that way may be fixated, in which case it is copied out of the home + directory, onto persistent storage, to fixate the UID/GID assignment of the record, and extract + additional (typically previously encrypted) user record data from the home directory. A home directory + mus be fixated before it can be logged into. This method call takes a user name and a JSON user record + consisting only of the secret section as argument. This method is equivalent to + Fixate() on the org.freedesktop.home1.Home interface. + + AuthenticateHome() checks passwords or other authentication credentials + associated with the home directory. It takes a user name and a JSON user record consisting only of the + secret section as argument. Note that many of the other method calls authenticate + the user first, in order to execute some other operation. This method call only authenticates and + executes no further operation. Like ActivateHome() it is usually first invoked + with an empty JSON user record, which is then populated for subsequent tries with additional + authentication data supplied. This method is equivalent to + Authenticate() on the org.freedesktop.home1.Home + interface. + + UpdateHome() updates a locally registered user record. Takes a fully + specified JSON user record as argument (including the secret section). A user with a + matching name and realm must be registered locally already, and the last change timestamp of the newly + supplied record must be newer than the previously existing user record. Note this operation updates the + user record only, it does not propagate passwords/authentication tokens from the user record to the + storage back-end, or resizes the storage back-end. Typically a home directory is first updated, and then + the password of the underlying storage updated using ChangePasswordHome() as well + as the storage resized using ResizeHome(). This method is equivalent to + Update() on the org.freedesktop.home1.Home interface. + + ResizeHome() resizes the storage associated with a user record. Takes a user + name, a disk size in bytes and a user record consisting only of the secret section + as argument. If the size is specified as UINT64_MAX the storage is resized to the + size already specified in the user record. Typically, if the user record is updated using + UpdateHome() above this is used to propagate the size configured there-in down to + the underlying storage back-end. This method is equivalent to + Resize() on the org.freedesktop.home1.Home + interface. + + ChangePasswordHome() changes the passwords/authentication tokens of a home + directory. Takes a user name, and two JSON user record objects, each consisting only of the + secret section, for the old and for the new passwords/authentication tokens. If the + user record with the new passwords/authentication token data is specified as empty the existing user + record's settings are propagated down to the home directory storage. This is typically used after a + user record is updated using UpdateHome() in order to propagate the + secrets/authentication tokens down to the storage. This method is equivalent to + ChangePassword() on the org.freedesktop.home1.Home + interface. + + LockHome() temporarily suspends access to a home directory, flushing out any + cryptographic keys from memory. This is only supported on some back-ends, and usually done during system + suspend, in order to effectively secure home directories while the system is sleeping. Takes a user + name as single argument. If an application attempts to access a home directory while it is locked it + will typically freeze until the home directory is unlocked again. This method is equivalent to + Lock() on the org.freedesktop.home1.Home interface. + + UnlockHome() undoes the effect of LockHome(). Takes a + user name and a user record consisting only of the secret section as arguments. This + method is equivalent to Unlock() on the + org.freedesktop.home1.Home interface. + + AcquireHome() activates or unlocks a home directory in a reference counted + mode of operation. Takes a user name and user record consisting only of secret + section as argument. If the home directory is not active yet, it is activated. If it is currently + locked it is unlocked. After completion a reference to the activation/unlocking of the home directory + is returned via a file descriptor. When the last client which acquired such a file descriptor closes it + the home directory is automatically deactivated again. This method is typically invoked when a user + logs in, and the file descriptor is held until the user logs out again, thus ensuring the user's home + directory can be unmounted automatically again in a robust fashion, when the user logs out. The third + argument is a boolean which indicates whether the client invoking the call is able to automatically + re-authenticate when the system comes back from suspending. It should be set by all clients that + implement a secure lock screen running outside of the user's context, that is brought up when the + system comes back from suspend and can be used to re-acquire the credentials to unlock the user's home + directory. If a home directory has at least one client with an open reference to the home directory + that does not support this it is not suspended automatically at system suspend, otherwise it is. This + method is equivalent to Acquire() on the + org.freedesktop.home1.Home interface. + + RefHome() is similar to AcquireHome() but takes no user + record with secret section, i.e. will take an additional reference to an already + activated/unlocked home directory without attempting to activate/unlock it itself. It will fail if the + home directory is not already activated. This method is equivalent to + Ref() on the org.freedesktop.home1.Home + interface. + + ReleaseHome() releases a home directory again, if all file descriptors + referencing it are already closed, that where acquired through AcquireHome() or + RefHome(). Note that this call does not actually cause the deactivation of the + home directory (which happens automatically when the last referencing file descriptor is closed), but + is simply a synchronization mechanism that allows delaying of the user session's termination until any + triggered deactivation is completed. This method is equivalent to Release() on the + org.freedesktop.home1.Home interface. + + LockAllHomes() locks all active home directories that only have references + that opted into automatic suspending during system suspend. This is usually invoked automatically + shortly before system suspend. + + + + Properties + + AutoLogin exposes an array of structures consisting of user name, seat name + and object path of an home directory object. All locally managed users that have the + autoLogin field set are listed here, with the seat name they are associated with. A + display manager may watch this property and pre-fill the login screen with the users exposed this + way. + + + + + The Home Object + + +node /org/freedesktop/home1/home { + interface org.freedesktop.home1.Home { + methods: + Activate(in s secret); + Deactivate(); + Unregister(); + Realize(in s secret); + Remove(); + Fixate(in s secret); + Authenticate(in s secret); + Update(in s user_record); + Resize(in t size, + in s secret); + ChangePassword(in s new_secret, + in s old_secret); + Lock(); + Unlock(in s secret); + Acquire(in s secret, + in b please_suspend, + out h send_fd); + Ref(in b please_suspend, + out h send_fd); + Release(); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UserName = '...'; + readonly u UID = ...; + readonly (suusss) UnixRecord = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s State = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly (sb) UserRecord = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.DBus.ObjectManager { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Activate(), Deactivate(), + Unregister(), Realize(), Remove(), + Fixate(), Authenticate(), Update(), + Resize(), ChangePassword(), Lock(), + Unlock(), Acquire(), Ref(), + Release() operate like their matching counterparts on the + org.freedesktop.home1.Manager interface (see above). The main difference is that + they are methods of the home directory objects, and hence carry no additional user name + parameter. Which of the two flavors of methods to call depends on the handles to the user known on the + client side: if only the user name is known, it's preferable to use the methods on the manager object + since they operate with user names only. If however the home object path was already acquired some way + it is preferable to operate on the org.freedesktop.home1.Home objects + instead. + + + + Properties + + UserName contains the user name of the user account/home directory. + + UID contains the numeric UNIX UID of the user account. + + UnixRecord contains a structure encapsulating the six fields a + struct passwd typically contains (the password field is suppressed). + + State exposes the current state home the home directory. + + UserRecord contains the full JSON user record string of the user account. + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + + + See Also + + systemd1, + systemd-homed.service8, + homectl1 + + + + diff --git a/man/org.freedesktop.hostname1.xml b/man/org.freedesktop.hostname1.xml new file mode 100644 index 000000000..6b2341e48 --- /dev/null +++ b/man/org.freedesktop.hostname1.xml @@ -0,0 +1,367 @@ + + + +%entities; +]> + + + + + org.freedesktop.hostname1 + systemd + + + + org.freedesktop.hostname1 + 5 + + + + org.freedesktop.hostname1 + The D-Bus interface of systemd-hostnamed + + + + Introduction + + + systemd-hostnamed.service8 + is a system service that can be used to control the hostname and related machine metadata from user + programs. This page describes the hostname semantics and the D-Bus interface. + + + + The D-Bus API + + The service exposes the following interfaces on the bus: + + +node /org/freedesktop/hostname1 { + interface org.freedesktop.hostname1 { + methods: + SetHostname(in s hostname, + in b interactive); + SetStaticHostname(in s hostname, + in b interactive); + SetPrettyHostname(in s hostname, + in b interactive); + SetIconName(in s icon, + in b interactive); + SetChassis(in s chassis, + in b interactive); + SetDeployment(in s deployment, + in b interactive); + SetLocation(in s location, + in b interactive); + GetProductUUID(in b interactive, + out ay uuid); + properties: + readonly s Hostname = '...'; + readonly s StaticHostname = '...'; + readonly s PrettyHostname = '...'; + readonly s IconName = '...'; + readonly s Chassis = '...'; + readonly s Deployment = '...'; + readonly s Location = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KernelName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KernelRelease = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KernelVersion = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s OperatingSystemPrettyName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s OperatingSystemCPEName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HomeURL = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Whenever the hostname or other metadata is changed via the daemon, + PropertyChanged signals are sent out to subscribed clients. Changing a hostname + using this interface is authenticated via + polkit. + + + + Semantics + + The static (configured) hostname is the one configured in + /etc/hostname. It is chosen by the local user. It is not always in sync with the + current hostname as returned by the + gethostname3 + system call. If no hostname is configured this property will be the empty string. Setting this property + to the empty string will remove /etc/hostname. This property should be an + internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces. + + The transient (dynamic) hostname is the one configured via the kernel's + sethostname3. + It can be different from the static hostname if DHCP or mDNS have been configured to change the name + based on network information. + This property is never empty. If no hostname is set this will default to + &FALLBACK_HOSTNAME; (configurable at compilation time). Setting this property to the + empty string will reset the dynamic hostname to the static hostname. If no static hostname is + configured the dynamic hostname will be reset to &FALLBACK_HOSTNAME;. This property + should be an internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces. + + The pretty hostname is a free-form UTF-8 hostname for presentation to the + user. User interfaces should ensure that the pretty hostname and the static hostname stay in sync. + I.e. when the former is Lennart’s Computer the latter should be + lennarts-computer. If no pretty hostname is set this setting will be the empty + string. Applications should then find a suitable fallback, such as the dynamic hostname. + + The icon name is a name following the XDG icon naming spec. If not set, + information such as the chassis type (see below) is used to find a suitable fallback icon name + (i.e. computer-laptop vs. computer-desktop is picked based on the + chassis information). If no such data is available, the empty string is returned. In that case an application + should fall back to a replacement icon, for example computer. If this property is set + to the empty string, the automatic fallback name selection is enabled again. + + The chassis type should be one of the currently defined chassis types: + desktop, laptop, server, + tablet, handset, as well as the special chassis types + vm and container for virtualized systems. Note that in most cases + the chassis type will be determined automatically from DMI/SMBIOS/ACPI firmware information. Writing to + this setting is hence useful only to override misdetected chassis types, or to configure the chassis type if + it could not be auto-detected. Set this property to the empty string to reenable the automatic detection of + the chassis type from firmware information. + + Note that systemd-hostnamed starts only on request and terminates after a + short idle period. This effectively means that PropertyChanged messages are not sent + out for changes made directly on the files (as in: administrator edits the files with vi). This is + the intended behavior: manual configuration changes should require manual reloading. + + The transient (dynamic) hostname maps directly to the kernel hostname. This hostname should be + assumed to be highly dynamic, and hence should be watched directly, without depending on + PropertyChanged messages from systemd-hostnamed. To accomplish + this, open /proc/sys/kernel/hostname and + poll3 + for SIGHUP which is triggered by the kernel every time the hostname changes. Again: + this is special for the transient (dynamic) hostname, and does not apply to the configured (fixed) + hostname. + + Applications may read the hostname data directly if hostname change notifications + are not necessary. Use + gethostname3, + /etc/hostname (possibly with per-distribution fallbacks), and + machine-info3 + for that. For more information on these files and syscalls see the respective man pages. + + + Methods and Properties + + SetHostname() sets the transient (dynamic) hostname which is exposed by the + Hostname property. If empty, the transient hostname is set to the static hostname. + + + SetStaticHostname() sets the static hostname which is exposed by the + StaticHostname property. If empty, the built-in default of + &FALLBACK_HOSTNAME; is used. + + SetPrettyHostname() sets the pretty hostname which is exposed by the + PrettyHostname property. + + SetIconName(), SetChassis(), + SetDeployment(), and SetLocation() set the properties + IconName (the name of the icon representing for the machine), + Chassis (the machine form factor), Deployment (the system + deployment environment), and Location (physical system location), respectively. + + + PrettyHostname, IconName, Chassis, + Deployment, and Location are stored in + /etc/machine-info. See + machine-info5 for + the semantics of those settings. + + GetProductUUID() returns the "product uuid" as exposed by the kernel based + on DMI information in /sys/class/dmi/id/product_uuid. Reading the file directly + requires root privileges, and this method allows access to unprivileged clients through the polkit + framework. + + KernelName, KernelRelease, and + KernelVersion expose the kernel name (e.g. Linux), release + (e.g. 5.0.0-11), and version (i.e. the build number, e.g. #11) as + reported by + uname2. + OperatingSystemPrettyName, OperatingSystemCPEName, and + HomeURL expose the PRETTY_NAME=, CPE_NAME= and + HOME_URL= fields from + os-release5. The + purpose of those properties is to allow remote clients to access this information over D-Bus. Local + clients can access the information directly. + + + + Security + + The interactive boolean parameters can be used to control whether polkit + should interactively ask the user for authentication credentials if required. + + The polkit action for SetHostname() is + org.freedesktop.hostname1.set-hostname. For + SetStaticHostname() and SetPrettyHostname() it is + org.freedesktop.hostname1.set-static-hostname. For + SetIconName() and SetChassis() it is + org.freedesktop.hostname1.set-machine-info. + + + + + Recommendations + + Here are three examples that show how the pretty hostname and the icon name should be used: + + When registering DNS-SD services: use the pretty hostname in the service name, and pass + the icon name in the TXT data, if there is an icon name. Browsing clients can then show the server icon + on each service. This is especially useful for WebDAV applications or UPnP media sharing. + + + Set the bluetooth name to the pretty hostname. + + When your file browser has a "Computer" icon, replace the name with the pretty hostname + if set, and the icon with the icon name, if it is set. + + + To properly handle name lookups with changing local hostnames without having to edit + /etc/hosts, we recommend using systemd-hostnamed in combination + with nss-myhostname3. + + + A client that wants to change the local hostname for DHCP/mDNS should invoke + SetHostname("newname", false) as soon as the name is available and afterwards reset it via + SetHostname(""). + + Here are some recommendations to follow when generating a static (internet) hostname from a pretty + name: + + Generate a single DNS label only, not an FQDN. That means no dots allowed. Strip them, + or replace them with -. + + It's probably safer to not use any non-ASCII chars, even if DNS allows this in some way + these days. In fact, restrict your charset to a-zA-Z0-9 and -. + Strip other chars, or try to replace them in some smart way with chars from this set, for example + äae, and use - as the replacement for all + punctuation characters and whitespace. + + Try to avoid creating repeated -, as well as - as + the first or last char. + + Limit the hostname to 63 chars, which is the length of a DNS label. + + If after stripping special chars the empty string is the result, you can pass this + as-is to systemd-hostnamed in which case it will automatically use + &FALLBACK_HOSTNAME;. + + Uppercase charaacters should be replaced with their lowercase equivalents. + + + + Note that while systemd-hostnamed applies some checks to the hostname you pass + they are much looser than the recommendations above. For example, systemd-hostnamed + will also accept _ in the hostname, but we recommend not using this to avoid clashes + with DNS-SD service types. Also systemd-hostnamed allows longer hostnames, but + because of the DNS label limitations, we recommend not making use of this. + + Here are a couple of example conversions: + + Lennart's PClennarts-pc + Müllers Computermuellers-computer + Voran!voran + Es war einmal ein Männleines-war-einmal-ein-maennlein + Jawoll. Ist doch wahr!jawoll-ist-doch-wahr + レナートlocalhost + ...zack!!! zack!...zack-zack + + + Of course, an already valid internet hostname label you enter and pass through this + conversion should stay unmodified, so that users have direct control of it, if they want — by simply + ignoring the fact that the pretty hostname is pretty and just edit it as if it was the normal internet + name. + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + + + Examples + + + Introspect <interfacename>org.freedesktop.hostname1</interfacename> on the bus + + $ gdbus introspect --system \ + --dest org.freedesktop.hostname1 \ + --object-path /org/freedesktop/hostname1 + + + + + + See also + + David Zeuthen's original Fedora + Feature page about xdg-hostname + + diff --git a/man/org.freedesktop.import1.xml b/man/org.freedesktop.import1.xml new file mode 100644 index 000000000..56ce9f0b4 --- /dev/null +++ b/man/org.freedesktop.import1.xml @@ -0,0 +1,348 @@ + + + + + + + org.freedesktop.import1 + systemd + + + + org.freedesktop.import1 + 5 + + + + org.freedesktop.import1 + The D-Bus interface of systemd-importd + + + + Introduction + + + systemd-importd.service8 + is a system service which may be used to import, export and download additional system images. These + images can be used by tools such as + systemd-nspawn1 + to run local containers. The service is used as the backend for machinectl pull-raw, + machinectl pull-tar and related commands. This page describes the D-Bus interface. + + + Note that + systemd-importd.service8 + is mostly a small companion service for + systemd-machined.service8. + Many operations to manipulate local container and VM images are hence available via the systemd-machined D-Bus API, c.f. + org.freedesktop.machine15. + + + + + The Manager Object + + The service exposes the following interfaces on the Manager object on the bus: + + +node /org/freedesktop/import1 { + interface org.freedesktop.import1.Manager { + methods: + ImportTar(in h fd, + in s local_name, + in b force, + in b read_only, + out u transfer_id, + out o transfer_path); + ImportRaw(in h fd, + in s local_name, + in b force, + in b read_only, + out u transfer_id, + out o transfer_path); + ImportFileSystem(in h fd, + in s local_name, + in b force, + in b read_only, + out u transfer_id, + out o transfer_path); + ExportTar(in s local_name, + in h fd, + in s format, + out u transfer_id, + out o transfer_path); + ExportRaw(in s local_name, + in h fd, + in s format, + out u transfer_id, + out o transfer_path); + PullTar(in s url, + in s local_name, + in s verify_mode, + in b force, + out u transfer_id, + out o transfer_path); + PullRaw(in s url, + in s local_name, + in s verify_mode, + in b force, + out u transfer_id, + out o transfer_path); + ListTransfers(out a(usssdo) transfers); + CancelTransfer(in u transfer_id); + signals: + TransferNew(u transfer_id, + o transfer_path); + TransferRemoved(u transfer_id, + o transfer_path, + s result); + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + ImportTar() and ImportRaw() import a system image and + place it into /var/lib/machines/. The first argument should be a file descriptor + (opened for reading) referring to the tar or raw file to import. It should reference a file on disk, + a pipe or a socket. When ImportTar() is used the file descriptor should + refer to a tar file, optionally compressed with + gzip1, + bzip21, + or + xz1. + systemd-importd will detect the used compression scheme (if any) automatically. When + ImportRaw() is used the file descriptor should refer to a raw or qcow2 disk image + containing an MBR or GPT disk label, also optionally compressed with gzip, bzip2 or xz. In either case, + if the file is specified as a file descriptor on disk, progress information is generated for the import + operation (as in that case we know the total size on disk). If a socket or pipe is specified, progress information is not + available. The file descriptor argument is followed by a local name for the image. This should be a + name suitable as a hostname and will be used to name the imported image below + /var/lib/machines. A tar import is placed as a directory tree or a + btrfs8 + subvolume below /var/lib/machines/ under the specified name with no suffix + appended. A raw import is placed as a file in /var/lib/machines/ with the + .raw suffix appended. If the argument is true, any + pre-existing image with the same name is removed before starting the operation. Otherwise, the + operation fails if an image with the same name already exists. Finally, the + argument controls + whether to create a writable or read-only image. Both methods return immediately after starting the import, + with the import transfer ongoing. They return a pair of transfer identifier and object path, which may + be used to retrieve progress information about the transfer or to cancel it. The transfer identifier is a + simple numeric identifier, the object path references an + org.freedesktop.import1.Transfer object, see below. Listen for a + TransferRemoved signal for the transfer ID in order to detect when a transfer is + complete. The returned transfer object is useful to determine the current progress or log output of the + ongoing import operation. + + ExportTar() and ExportRaw() implement the reverse + operation, and may be used to export a system image in order to place it in a tar or raw image. They + take the machine name to export as their first parameter, followed by a file descriptor (opened for writing) + where the tar or raw file will be written. It may either reference a file on disk or a pipe/socket. The + third argument specifies in which compression format to write the image. It takes one of + uncompressed, xz, bzip2 or + gzip, depending on which compression scheme is required. The image written to the + specified file descriptor will be a tar file in case of ExportTar() or a raw disk + image in case of ExportRaw(). Note that currently raw disk images may not be + exported as tar files, and vice versa. This restriction might be lifted eventually. The method + returns a transfer identifier and object path for cancelling or tracking the export operation, similar + to ImportTar() or ImportRaw() as described above. + + PullTar() and PullRaw() may be used to download, verify + and import a system image from a URL. They take an URL argument which should point to a tar or + raw file on the http:// or https:// protocols, possibly + compressed with xz, bzip2 or gzip. The second argument is a local name for the image. It should be + suitable as a hostname, similar to the matching argument of the ImportTar() and + ImportRaw() methods above. The third argument indicates the verification mode for + the image. It may be one of no, checksum, + signature. no turns off any kind of verification of the image; + checksum looks for a SHA256SUM file next to the downloaded + image and verifies any SHA256 hash value in that file against the image; signature + does the same but also tries to authenticate the SHA256SUM file via + gpg8 + first. The last argument indicates whether to replace a possibly pre-existing image with the same local + name (if true), or whether to fail (if false). Like the import + and export calls above, these calls return a pair of transfer identifier and object path for the ongoing + download. + + ListTransfers() returns a list of ongoing import, export or download + operations as created with the six calls described above. It returns an array of structures which + consist of the numeric transfer identifier, a string indicating the operation (one of + import-tar, import-raw, export-tar, + export-raw, pull-tar or pull-raw), a string + describing the remote file (in case of download operations this is the source URL, in case of + import/export operations this is a short string describing the file descriptor passed in), a string + with the local machine image name, a progress value between 0.0 (for 0%) and 1.0 (for 100%), as well as + the transfer object path. + + CancelTransfer() may be used to cancel an ongoing import, export or download + operation. Simply specify the transfer identifier to cancel the ongoing operation. + + + + Signals + + The TransferNew signal is generated each time a new transfer is started with + the import, export or download calls described above. It carries the transfer ID and object path that + have just been created. + + The TransferRemoved signal is sent each time a transfer finishes, + is canceled or fails. It also carries the transfer ID and object path, followed by a string indicating + the result of the operation, which is one of done (on success), + canceled or failed. + + + + + The Transfer Object + + +node /org/freedesktop/import1/transfer/_1 { + interface org.freedesktop.import1.Transfer { + methods: + Cancel(); + signals: + LogMessage(u priority, + s line); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Id = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Local = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Remote = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Type = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Verify = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly d Progress = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + The Cancel() method may be used to cancel the transfer. It takes no + parameters. This method is pretty much equivalent to the CancelTransfer() method + on the Manager interface (see above), but is exposed on the + Transfer object itself instead of taking a transfer ID. + + + + Properties + + The Id property exposes the numeric transfer ID of the transfer object. + + The Local, Remote and Type properties + expose the local container name of this transfer, the remote source (in case of download: the URL, in + case of import/export: a string describing the file descriptor passed in), and the type of operation + (see the Manager's ListTransfer() method above for an explanation of the possible + values). + + The Verify property exposes the selected verification setting and is only + defined for download operations (see above). + + The Progress property exposes the current progress of the transfer as a value + between 0.0 and 1.0. To show a progress bar on screen we recommend to query this value in regular + intervals, for example every 500 ms or so. + + + + + Examples + + + Introspect <interfacename>org.freedesktop.import1.Manager</interfacename> on the bus + + $ gdbus introspect --system \ + --dest org.freedesktop.import1 \ + --object-path /org/freedesktop/import1 + + + + + Introspect <interfacename>org.freedesktop.import1.Transfer</interfacename> on the bus + + $ gdbus introspect --system \ + --dest org.freedesktop.import1 \ + --object-path /org/freedesktop/import1/transfer/_1 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.locale1.xml b/man/org.freedesktop.locale1.xml new file mode 100644 index 000000000..3956eaf8a --- /dev/null +++ b/man/org.freedesktop.locale1.xml @@ -0,0 +1,188 @@ + + + + + + + org.freedesktop.locale1 + systemd + + + + org.freedesktop.locale1 + 5 + + + + org.freedesktop.locale1 + The D-Bus interface of systemd-localed + + + + Introduction + + + systemd-localed.service8 + is a system service that can be used to control the system locale and keyboard mapping from user + programs. This page describes the D-Bus interface. + + + + The D-Bus API + + The service exposes the following interfaces on the bus: + + +node /org/freedesktop/locale1 { + interface org.freedesktop.locale1 { + methods: + SetLocale(in as locale, + in b interactive); + SetVConsoleKeyboard(in s keymap, + in s keymap_toggle, + in b convert, + in b interactive); + SetX11Keyboard(in s layout, + in s model, + in s variant, + in s options, + in b convert, + in b interactive); + properties: + readonly as Locale = ['...', ...]; + readonly s X11Layout = '...'; + readonly s X11Model = '...'; + readonly s X11Variant = '...'; + readonly s X11Options = '...'; + readonly s VConsoleKeymap = '...'; + readonly s VConsoleKeymapToggle = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + If you set a new system locale all old system locale settings will be dropped and the new + settings will be saved to disk. The locale will also be passed to the system manager, and subsequently started + daemons will inherit the new system locale. Note that already running daemons will not learn about the + new value. + + The SetVConsoleKeyboard() method may be used to set the key mapping for the + virtual console. Similarly, SetX11Keyboard() may be used to set the default key + mapping of any X11 servers. + + Note that SetVConsoleKeyboard() instantly applies the new key mapping to the + console, while SetX11Keyboard() simply sets a default that may be used by later + sessions. + + The boolean argument convert may be set to optionally convert the console + keyboard configuration to X11 keyboard mappings and vice versa. If true and + SetVConsoleKeyboard() is used, the nearest X11 keyboard setting for the chosen + console setting is set. If true and SetX11Keyboard() is used, the nearest console + keyboard setting for the chosen X11 setting is set. Hence, it is usually sufficient to call only one of the + two functions. + + For graphical UIs that need to set the system keyboard mapping simply invoke + SetX11Keyboard(), set convert=true and ignore + SetVConsoleKeyboard(). + + Use the empty string for the keymap parameters you wish not to set. + + The interactive boolean parameters can be used to control whether + polkit + should interactively ask the user for authentication credentials if required. + + + + Signals + + Whenever the system locale or keymap is changed via the daemon, + PropertyChanged signals are sent out to which clients can subscribe. + + + + Properties + + The system locale is shown in the Locale property. It is an array containing + environment-variable-assignment-like strings. The following strings are known: + LANG=, LC_CTYPE=, LC_NUMERIC=, + LC_TIME=, LC_COLLATE=, LC_MONETARY=, + LC_MESSAGES=, LC_PAPER=, LC_NAME=, + LC_ADDRESS=, LC_TELEPHONE=, LC_MEASUREMENT=, + LC_IDENTIFICATION=. + + The X11Layout, X11Model, X11Variant, and + X11Options properties show values configurable with + SetX11Keyboard() described above (or SetVConsoleKeyboard() + with convert=true). The VConsoleKeymap and + VConsoleKeymapToggle properties show values configurable with + SetVConsoleKeyboard() (or SetX11Keyboard() with + convert=true). + + + + Security + + Changing the system locale or keymap using this interface is authenticated via polkit. The + polkit action for SetLocale() is + org.freedesktop.locale1.set-locale. The polkit action for + SetX11Keyboard() and SetVConsoleKeyboard() is + org.freedesktop.locale1.set-keyboard. + + + + + Examples + + + Introspect <interfacename>org.freedesktop.locale1</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.locale1 \ + --object-path /org/freedesktop/locale1 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml new file mode 100644 index 000000000..1b7c3b642 --- /dev/null +++ b/man/org.freedesktop.login1.xml @@ -0,0 +1,1390 @@ + + + + + + + org.freedesktop.login1 + systemd + + + + org.freedesktop.login1 + 5 + + + + org.freedesktop.login1 + The D-Bus interface of systemd-logind + + + + Introduction + + systemd-logind.service8 + is a system service that keeps track of user logins and seats. + + The daemon provides both a C library interface as well as a D-Bus interface. The library interface + may be used to introspect and watch the state of user logins and seats. The bus interface provides the + same functionality but in addition may also be used to make changes to the system state. For more information please + consult sd-login3. + + + + + The Manager Object + + The service exposes the following interfaces on the Manager object on the bus: + + +node /org/freedesktop/login1 { + interface org.freedesktop.login1.Manager { + methods: + GetSession(in s session_id, + out o object_path); + GetSessionByPID(in u pid, + out o object_path); + GetUser(in u uid, + out o object_path); + GetUserByPID(in u pid, + out o object_path); + GetSeat(in s seat_id, + out o object_path); + ListSessions(out a(susso) sessions); + ListUsers(out a(uso) users); + ListSeats(out a(so) seats); + ListInhibitors(out a(ssssuu) inhibitors); + CreateSession(in u uid, + in u pid, + in s service, + in s type, + in s class, + in s desktop, + in s seat_id, + in u vtnr, + in s tty, + in s display, + in b remote, + in s remote_user, + in s remote_host, + in a(sv) properties, + out s session_id, + out o object_path, + out s runtime_path, + out h fifo_fd, + out u uid, + out s seat_id, + out u vtnr, + out b existing); + ReleaseSession(in s session_id); + ActivateSession(in s session_id); + ActivateSessionOnSeat(in s session_id, + in s seat_id); + LockSession(in s session_id); + UnlockSession(in s session_id); + LockSessions(); + UnlockSessions(); + KillSession(in s session_id, + in s who, + in i signal_number); + KillUser(in u uid, + in i signal_number); + TerminateSession(in s session_id); + TerminateUser(in u uid); + TerminateSeat(in s seat_id); + SetUserLinger(in u uid, + in b enable, + in b interactive); + AttachDevice(in s seat_id, + in s sysfs_path, + in b interactive); + FlushDevices(in b interactive); + PowerOff(in b interactive); + Reboot(in b interactive); + Halt(in b interactive); + Suspend(in b interactive); + Hibernate(in b interactive); + HybridSleep(in b interactive); + SuspendThenHibernate(in b interactive); + CanPowerOff(out s result); + CanReboot(out s result); + CanHalt(out s result); + CanSuspend(out s result); + CanHibernate(out s result); + CanHybridSleep(out s result); + CanSuspendThenHibernate(out s result); + ScheduleShutdown(in s type, + in t usec); + CancelScheduledShutdown(out b cancelled); + Inhibit(in s what, + in s who, + in s why, + in s mode, + out h pipe_fd); + CanRebootParameter(out s result); + SetRebootParameter(in s parameter); + CanRebootToFirmwareSetup(out s result); + SetRebootToFirmwareSetup(in b enable); + CanRebootToBootLoaderMenu(out s result); + SetRebootToBootLoaderMenu(in t timeout); + CanRebootToBootLoaderEntry(out s result); + SetRebootToBootLoaderEntry(in s boot_loader_entry); + SetWallMessage(in s wall_message, + in b enable); + signals: + SessionNew(s session_id, + o object_path); + SessionRemoved(s session_id, + o object_path); + UserNew(u uid, + o object_path); + UserRemoved(u uid, + o object_path); + SeatNew(s seat_id, + o object_path); + SeatRemoved(s seat_id, + o object_path); + PrepareForShutdown(b start); + PrepareForSleep(b start); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite b EnableWallMessages = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s WallMessage = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u NAutoVTs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as KillOnlyUsers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as KillExcludeUsers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b KillUserProcesses = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s RebootParameter = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b RebootToFirmwareSetup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t RebootToBootLoaderMenu = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s RebootToBootLoaderEntry = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as BootLoaderEntries = ['...', ...]; + readonly b IdleHint = ...; + readonly t IdleSinceHint = ...; + readonly t IdleSinceHintMonotonic = ...; + readonly s BlockInhibited = '...'; + readonly s DelayInhibited = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InhibitDelayMaxUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UserStopDelayUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandlePowerKey = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandleSuspendKey = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandleHibernateKey = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandleLidSwitch = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandleLidSwitchExternalPower = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s HandleLidSwitchDocked = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t HoldoffTimeoutUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s IdleAction = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t IdleActionUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b PreparingForShutdown = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b PreparingForSleep = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly (st) ScheduledShutdown = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Docked = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b LidClosed = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b OnExternalPower = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RuntimeDirectorySize = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RuntimeDirectoryInodesMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InhibitorsMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t NCurrentInhibitors = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SessionsMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t NCurrentSessions = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + GetSession() may be used to get the session object path for the session with + the specified ID. Similarly, GetUser() and GetSeat() get the + user and seat objects, respectively. GetSessionByPID() and + GetUserByPID() get the session/user object the specified PID belongs to if there + is any. + + ListSessions() returns an array of all current sessions. The structures in + the array consist of the following fields: session id, user id, user name, seat id, session object + path. If a session does not have a seat attached, the seat id field will be an empty string. + + ListUsers() returns an array of all currently logged in users. The + structures in the array consist of the following fields: user id, user name, user object path. + + ListSeats() returns an array of all currently available seats. The + structure in the array consists of the following fields: seat id, seat object path. + + ListInhibitors() lists all currently active inhibitors. It returns an array of + structures consisting of what, who, why, + mode, uid (user ID), and pid (process ID). + + CreateSession() and ReleaseSession() may be used to + open or close login sessions. These calls should never be invoked directly by + clients. Creating/closing sessions is exclusively the job of PAM and its + pam_systemd8 + module. + + ActivateSession() brings the session with the specified ID into the + foreground. ActivateSessionOnSeat() does the same, but only if the seat id + matches. + + LockSession() asks the session with the specified ID to activate the screen + lock. UnlockSession() asks the session with the specified ID to remove an active + screen lock, if there is any. This is implemented by sending out the Lock() and Unlock() signals from + the respective session object which session managers are supposed to listen on. + + LockSessions() asks all sessions to activate their screen locks. This may be + used to lock access to the entire machine in one action. Similarly, UnlockSessions() + asks all sessions to deactivate their screen locks. + + KillSession() may be used to send a Unix signal to one or all processes of a + session. As arguments it takes the session id, either the string leader or + all and a signal number. If leader is passed only the session + leader is killed. If all is passed all processes of the session + are killed. + + KillUser() may be used to send a Unix signal to all processes of a user. As + arguments it takes the user id and a signal number. + + TerminateSession(), TerminateUser(), + TerminateSeat() may be used to forcibly terminate one specific session, all + processes of a user, and all sessions attached to a specific seat, respectively. The session, user, + and seat are identified by their respective IDs. + + SetUserLinger() enables or disables user lingering. If enabled, the runtime + directory of a user is kept around and they may continue to run processes while logged out. If + disabled, the runtime directory goes away as soon as they log out. SetUserLinger() + expects three arguments: the UID, a boolean whether to enable/disable and a boolean controlling the + polkit + authorization interactivity (see below). Note that the user linger state is persistently + stored on disk. + + AttachDevice() may be used to assign a specific device to a specific + seat. The device is identified by its /sys path and must be eligible for seat + assignments. AttachDevice() takes three arguments: the seat id, the sysfs path, + and a boolean for controlling polkit interactivity (see below). Device assignments are persistently + stored on disk. To create a new seat, simply specify a previously unused seat id. For more information + about the seat assignment logic see + Multi-Seat for Linux. + + + FlushDevices() removes all explicit seat assignments for devices, resetting + all assignments to the automatic defaults. The only argument it takes is the polkit interactivity + boolean (see below). + + PowerOff(), Reboot(), Halt(), + Suspend(), and Hibernate() result in the system being powered + off, rebooted, halted (shut down without turning off power), suspended (the system state is + saved to RAM and the CPU is turned off), or hibernated (the system state is saved to disk and + the machine is powered down). HybridSleep() results in the system entering a + hybrid-sleep mode, i.e. the system is both hibernated and suspended. + SuspendThenHibernate() results in the system being suspended, then later woken + using an RTC timer and hibernated. The only argument is the polkit interactivity boolean + interactive (see below). The main purpose of these calls is that they enforce + polkit policy and hence allow powering off/rebooting/suspending/hibernating even by unprivileged + users. They also enforce inhibition locks. UIs should expose these calls as the primary mechanism to + poweroff/reboot/suspend/hibernate the machine. + + SetRebootParameter() sets a parameter for a subsequent reboot operation. + See the description of reboot in + systemctl1 and + reboot2 + for more information. + + SetRebootToFirmwareSetup(), + SetRebootToBootLoaderMenu(), and SetRebootToBootLoaderEntry() + configure the action to be taken from the boot loader after a reboot: respectively entering firmware + setup mode, the boot loader menu, or a specific boot loader entry. See + systemctl1 for the + corresponding command line interface. + + CanPowerOff(), CanReboot(), + CanHalt(), CanSuspend(), CanHibernate(), + CanHybridSleep(), CanSuspendThenHibernate(), + CanRebootParameter(), CanRebootToFirmwareSetup(), + CanRebootToBootLoaderMenu(), and + CanRebootToBootLoaderEntry() test whether the system supports the respective + operation and whether the calling user is allowed to execute it. Returns one of na, + yes, no, and challenge. If + na is returned, the operation is not available because hardware, kernel, or drivers + do not support it. If yes is returned, the operation is supported and the user may + execute the operation without further authentication. If no is returned, the + operation is available but the user is not allowed to execute the operation. If + challenge is returned, the operation is available but only after + authorization. + + ScheduleShutdown() schedules a shutdown operation type at + time usec in microseconds since the UNIX epoch. type can be one + of poweroff, dry-poweroff, reboot, + dry-reboot, halt, and dry-halt. (The + dry- variants do not actually execute the shutdown action.) + CancelScheduledShutdown() cancels a scheduled shutdown. The output parameter + cancelled is true if a shutdown operation was scheduled. + + SetWallMessage() sets the wall message (the message that will be sent out to + all terminals and stored in a + utmp5 record) for a + subsequent scheduled shutdown operation. The parameter wall_message specifies the + shutdown reason (and may be empty) which will be included in the shutdown message. The parameter + enable specifies whether to print a wall message on shutdown. + + Inhibit() creates an inhibition lock. It takes four parameters: + what, who, why, and + mode. what is one or more of shutdown, + sleep, idle, handle-power-key, + handle-suspend-key, handle-hibernate-key, + handle-lid-switch, separated by colons, for inhibiting poweroff/reboot, + suspend/hibernate, the automatic idle logic, or hardware key handling. who should be + a short human readable string identifying the application taking the lock. why + should be a short human readable string identifying the reason why the lock is taken. Finally, + mode is either block or delay which encodes + whether the inhibit shall be consider mandatory or whether it should just delay the operation to a + certain maximum time. The method returns a file descriptor. The lock is released the moment this file + descriptor and all its duplicates are closed. For more information on the inhibition logic see + Inhibitor Locks. + + + + + Signals + + Whenever the inhibition state or idle hint changes, PropertyChanged + signals are sent out to which clients can subscribe. + + The SessionNew, SessionRemoved, + UserNew, UserRemoved, SeatNew, and + SeatRemoved signals are sent each time a session is created or removed, a user + logs in or out, or a seat is added or removed. They each contain the ID of the object plus the object + path. + + The PrepareForShutdown() and PrepareForSleep() signals + are sent right before (with the argument true) or after (with the argument + false) the system goes down for reboot/poweroff and suspend/hibernate, + respectively. This may be used by applications to save data on disk, release memory, or do other jobs + that should be done shortly before shutdown/sleep, in conjunction with delay inhibitor locks. After + completion of this work they should release their inhibition locks in order to not delay the operation + any further. For more information see + Inhibitor Locks. + + + + + Properties + + Most properties simply reflect the configuration, see + logind.conf5. This + includes: NAutoVTs, KillOnlyUsers, + KillExcludeUsers, KillUserProcesses, IdleAction, + InhibitDelayMaxUSec, + InhibitorsMax, + UserStopDelayUSec, + HandlePowerKey, HandleSuspendKey, + HandleHibernateKey, HandleLidSwitch, + HandleLidSwitchExternalPower, HandleLidSwitchDocked, + IdleActionUSec, HoldoffTimeoutUSec, + RemoveIPC, RuntimeDirectorySize, + RuntimeDirectoryInodesMax, InhibitorsMax, and + SessionsMax. + + + The IdleHint property reflects the idle hint state of the system. If the + system is idle it might get into automatic suspend or shutdown depending on the configuration. + + IdleSinceHint and IdleSinceHintMonotonic encode the + timestamps of the last change of the idle hint boolean, in CLOCK_REALTIME and + CLOCK_MONOTONIC timestamps, respectively, in microseconds since the epoch. + + The BlockInhibited and DelayInhibited properties encode + the currently active locks of the respective modes. They are colon separated lists of + shutdown, sleep, and idle (see above). + + NCurrentSessions and NCurrentInhibitors contain the number + of currently registered sessions and inhibitors. + + The BootLoaderEntries property contains a list of boot loader entries. + This includes boot loader entries defined in configuration and any additional loader entries + reported by the boot loader. See + systemd-boot7 + for more information. + + The PreparingForShutdown and PreparingForSleep boolean + properties are true during the interval between the two PrepareForShutdown and + PrepareForSleep signals respectively. Note that these properties do not + send out PropertyChanged signals. + + The RebootParameter property shows the value set with the + SetRebootParameter() method described above. + + ScheduledShutdown shows the value pair set with the + ScheduleShutdown() method described above. + + RebootToFirmwareSetup, RebootToBootLoaderMenu, and + RebootToBootLoaderEntry are true when the resprective post-reboot operation was + selected with SetRebootToFirmwareSetup, + SetRebootToBootLoaderMenu, or + SetRebootToBootLoaderEntry. + + The WallMessage and EnableWallMessages properties reflect the + shutdown reason and wall message enablement switch which can be set with the + SetWallMessage() method described above. + + Docked is true if the machine is connected to a dock. + LidClosed is true when the lid (of a laptop) is closed. + OnExternalPower is true when the machine is connected to an external power supply. + + + + + Security + + A number of operations are protected via the polkit privilege + system. SetUserLinger() requires the + org.freedesktop.login1.set-user-linger + privilege. AttachDevice() requires + org.freedesktop.login1.attach-device and + FlushDevices() requires + org.freedesktop.login1.flush-devices. PowerOff(), + Reboot(), Halt(), Suspend(), + Hibernate() require + org.freedesktop.login1.power-off, + org.freedesktop.login1.power-off-multiple-sessions, + org.freedesktop.login1.power-off-ignore-inhibit, + org.freedesktop.login1.reboot, + org.freedesktop.login1.reboot-multiple-sessions, + org.freedesktop.login1.reboot-ignore-inhibit, + org.freedesktop.login1.halt, + org.freedesktop.login1.halt-multiple-sessions, + org.freedesktop.login1.halt-ignore-inhibit, + org.freedesktop.login1.suspend, + org.freedesktop.login1.suspend-multiple-sessions, + org.freedesktop.login1.suspend-ignore-inhibit, + org.freedesktop.login1.hibernate, + org.freedesktop.login1.hibernate-multiple-sessions, + org.freedesktop.login1.hibernate-ignore-inhibit, + respectively depending on whether there are other sessions around or active inhibits are present. + HybridSleep() and SuspendThenHibernate() + use the same privileges as Hibernate(). + SetRebootParameter() requires + org.freedesktop.login1.set-reboot-parameter. + + SetRebootToFirmwareSetup requires + org.freedesktop.login1.set-reboot-to-firmware-setup. + SetRebootToBootLoaderMenu requires + org.freedesktop.login1.set-reboot-to-boot-loader-menu. + SetRebootToBootLoaderEntry requires + org.freedesktop.login1.set-reboot-to-boot-loader-entry. + + + ScheduleShutdown and CancelScheduledShutdown require + the same privileges (listed above) as the immediate poweroff/reboot/halt operations. + + Inhibit() is protected via one of + org.freedesktop.login1.inhibit-block-shutdown, + org.freedesktop.login1.inhibit-delay-shutdown, + org.freedesktop.login1.inhibit-block-sleep, + org.freedesktop.login1.inhibit-delay-sleep, + org.freedesktop.login1.inhibit-block-idle, + org.freedesktop.login1.inhibit-handle-power-key, + org.freedesktop.login1.inhibit-handle-suspend-key, + org.freedesktop.login1.inhibit-handle-hibernate-key, + org.freedesktop.login1.inhibit-handle-lid-switch depending on the lock + type and mode taken. + + The interactive boolean parameters can be used to control whether polkit + should interactively ask the user for authentication credentials if required. + + + + + Seat Objects + + +node /org/freedesktop/login1/seat/seat0 { + interface org.freedesktop.login1.Seat { + methods: + Terminate(); + ActivateSession(in s session_id); + SwitchTo(in u vtnr); + SwitchToNext(); + SwitchToPrevious(); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Id = '...'; + readonly (so) ActiveSession = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanTTY = ...; + readonly b CanGraphical = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(so) Sessions = [...]; + readonly b IdleHint = ...; + readonly t IdleSinceHint = ...; + readonly t IdleSinceHintMonotonic = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Terminate() and ActivateSession() work similar to + TerminateSeat(), ActivationSessionOnSeat() on the Manager object. + + SwitchTo() switches to the session on the virtual terminal + vtnr. SwitchToNext() and + SwitchToPrevious() switch to, respectively, the next and previous sessions on the + seat in the order of virtual terminals. If there is no active session, they switch to, respectively, + the first and last session on the seat. + + + + Signals + + Whenever ActiveSession, Sessions, + CanGraphical, CanTTY, + or the idle state changes, PropertyChanged signals are sent out to which clients + can subscribe. + + + + Properties + + The Id property encodes the ID of the seat. + + ActiveSession encodes the currently active session if there is one. It is a + structure consisting of the session id and the object path. + + CanTTY encodes whether the session is suitable for text logins, and + CanGraphical whether it is suitable for graphical sessions. + + The Sessions property is an array of all current sessions of this seat, each + encoded in a structure consisting of the ID and the object path. + + The IdleHint, IdleSinceHint, and + IdleSinceHintMonotonic properties encode the idle state, similar to the ones exposed + on the Manager object, but specific for this seat. + + + + + User Objects + + +node /org/freedesktop/login1/user/_1000 { + interface org.freedesktop.login1.User { + methods: + Terminate(); + Kill(in i signal_number); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u GID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Name = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t Timestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RuntimePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Service = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Slice = '...'; + readonly (so) Display = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s State = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(so) Sessions = [...]; + readonly b IdleHint = ...; + readonly t IdleSinceHint = ...; + readonly t IdleSinceHintMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Linger = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Terminate() and Kill() work similar to the + TerminateUser() and KillUser() methods on the manager + object. + + + + Signals + + Whenever Sessions or the idle state changes, + PropertyChanged signals are sent out to which clients can subscribe. + + + + Properties + + The UID and GID properties encode the Unix UID and primary + GID of the user. + + The Name property encodes the user name. + + Timestamp and TimestampMonotonic encode the login time of + the user in microseconds since the epoch, in the CLOCK_REALTIME and + CLOCK_MONOTONIC clocks, respectively. + + RuntimePath encodes the runtime path of the user, + i.e. $XDG_RUNTIME_DIR. For details see the + + XDG Basedir Specification + . + + Service contains the unit name of the user systemd service of this + user. Each logged in user is assigned a user service that runs a user systemd instance. This is + usually an instance of user@.service. + + Slice contains the unit name of the user systemd slice of this user. Each + logged in user gets a private slice. + + Display encodes which graphical session should be used as the primary UI display + for the user. It is a structure encoding the session ID and the object path of the session to use. + + State encodes the user state and is one of offline, + lingering, online, active, or + closing. See + sd_uid_get_state3 + for more information about the states. + + Sessions is an array of structures encoding all current sessions of the + user. Each structure consists of the ID and object path. + + The IdleHint, IdleSinceHint, and + IdleSinceHintMonotonic properties encode the idle hint state of the user, similar to + the Manager's properties, but specific for this user. + + The Linger property shows whether lingering is enabled for this user. + + + + + Session Objects + + +node /org/freedesktop/login1/session/1 { + interface org.freedesktop.login1.Session { + methods: + Terminate(); + Activate(); + Lock(); + Unlock(); + SetIdleHint(in b idle); + SetLockedHint(in b locked); + Kill(in s who, + in i signal_number); + TakeControl(in b force); + ReleaseControl(); + SetType(in s type); + TakeDevice(in u major, + in u minor, + out h fd, + out b inactive); + ReleaseDevice(in u major, + in u minor); + PauseDeviceComplete(in u major, + in u minor); + SetBrightness(in s subsystem, + in s name, + in u brightness); + signals: + PauseDevice(u major, + u minor, + s type); + ResumeDevice(u major, + u minor, + h fd); + Lock(); + Unlock(); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Id = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (uo) User = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Name = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t Timestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u VTNr = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (so) Seat = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TTY = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Display = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Remote = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RemoteHost = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RemoteUser = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Service = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Desktop = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Scope = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Leader = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Audit = ...; + readonly s Type = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Class = '...'; + readonly b Active = ...; + readonly s State = '...'; + readonly b IdleHint = ...; + readonly t IdleSinceHint = ...; + readonly t IdleSinceHintMonotonic = ...; + readonly b LockedHint = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Terminate(), Activate(), Lock(), + Unlock(), and Kill() work similarly to the respective calls on + the Manager object. + + SetIdleHint() is called by the session object to update the idle state + of the session whenever it changes. + + TakeControl() allows a process to take exclusive managed device + access-control for that session. Only one D-Bus connection can be a controller for a given session at any + time. If the force argument is set (root only), an existing controller is kicked + out and replaced. Otherwise, this method fails if there is already a controller. Note that this method is + limited to D-Bus users with the effective UID set to the user of the session or root. + + ReleaseControl() drops control of a given session. Closing the D-Bus + connection implicitly releases control as well. See TakeControl() for more + information. This method also releases all devices for which the controller requested ownership via + TakeDevice(). + + SetType() allows the type of the session to be changed dynamically. It can + only be called by session's current controller. If TakeControl() has not been + called, this method will fail. In addition, the session type will be reset to its original value once + control is released, either by calling ReleaseControl() or closing the D-Bus + connection. This should help prevent a session from entering an inconsistent state, for example if the + controller crashes. The only argument type is the new session type. + + TakeDevice() allows a session controller to get a file descriptor for a + specific device. Pass in the major and minor numbers of the character device and + systemd-logind will return a file descriptor for the device. Only a limited set of + device-types is currently supported (but may be extended). systemd-logind + automatically mutes the file descriptor if the session is inactive and resumes it once the session is + activated again. This guarantees that a session can only access session devices if the session is + active. Note that this revoke/resume mechanism is asynchronous and may happen at any given time. This + only works on devices that are attached to the seat of the given session. A process is not required to + have direct access to the device node. systemd-logind only requires you to be the + active session controller (see TakeControl()). Also note that any device can only + be requested once. As long as you don't release it, further TakeDevice() calls + will fail. + + ReleaseDevice() releases a device again (see + TakeDevice()). This is also implicitly done by + ReleaseControl() or when closing the D-Bus connection. + + PauseDeviceComplete() allows a session controller to synchronously pause a + device after receiving a PauseDevice(pause) signal. Forced + signals (or after an internal timeout) are automatically completed by + systemd-logind asynchronously. + + SetLockedHint() may be used to set the "idle hint" to + locked, i.e. information whether the session is locked. This is intended to be used + by the desktop environment to tell systemd-logind when the session is locked and + unlocked. + + SetBrightness() may be used to set the display brightness. This is intended + to be used by the desktop environment and allows unprivileged programs to access hardware settings in + a controlled way. The subsystem parameter specifies a kernel subsystem, either + backlight or leds. The name parameter + specifies a device name under the specified subsystem. The brightness parameter + specifies the brightness. The range is defined by individual drivers, see + /sys/class/subsystem/name/max_brightness. + + + + + Signals + + The active session controller exclusively gets PauseDevice and + ResumeDevice events for any device it requested via + TakeDevice(). They notify the controller whenever a device is paused or resumed. A + device is never resumed if its session is inactive. Also note that PauseDevice + signals are sent before the PropertyChanged signal for the + Active state. The inverse is true for ResumeDevice. A device + may remain paused for unknown reasons even though the Session is active. + + + A PauseDevice signal carries the major and minor numbers and a string describing the + type as arguments. force means the device was already paused by + systemd-logind and the signal is only an asynchronous + notification. pause means systemd-logind grants you a limited amount of time to pause the device. You must respond to this via + PauseDeviceComplete(). This synchronous pausing mechanism is used for + backwards-compatibility to VTs and systemd-logind is free to not make use of + it. It is also free to send a forced PauseDevice if you don't respond in a timely + manner (or for any other reason). gone means the device was unplugged from the + system and you will no longer get any notifications about it. There is no need to call + ReleaseDevice(). You may call TakeDevice() again if a new + device is assigned the major+minor combination. + + ResumeDevice is sent whenever a session is active and a device is + resumed. It carries the major/minor numbers as arguments and provides a new open file descriptor. You should + switch to the new descriptor and close the old one. They are not guaranteed to have the same underlying + open file descriptor in the kernel (except for a limited set of device types). + + Whenever Active or the idle state changes, + PropertyChanged signals are sent out to which clients can subscribe. + + Lock/Unlock is sent when the session is asked to be + screen-locked/unlocked. A session manager of the session should listen to this signal and act + accordingly. This signal is sent out as a result of the Lock() and + Unlock() methods, respectively. + + + + Properties + + Id encodes the session ID. + + User encodes the user ID of the user this session belongs to. This is a + structure consisting of the Unix UID and the object path. + + Name encodes the user name. + + Timestamp and TimestampMonotonic encode the microseconds + since the epoch when the session was created, in CLOCK_REALTIME or + CLOCK_MONOTONIC, respectively. + + VTNr encodes the virtual terminal number of the session if there is any, 0 + otherwise. + + Seat encodes the seat this session belongs to if there is any. This is a + structure consisting of the ID and the seat object path. + + TTY encodes the kernel TTY path of the session if this is a text login. If not + this is an empty string. + + Display encodes the X11 display name if this is a graphical login. If not, + this is an empty string. + + Remote encodes whether the session is local or remote. + + RemoteHost and RemoteUser encode the remote host and user + if this is a remote session, or an empty string otherwise. + + Service encodes the PAM service name that registered the session. + + Desktop describes the desktop environment running in the session (if + known). + + Scope contains the systemd scope unit name of this session. + + Leader encodes the PID of the process that registered the session. + + Audit encodes the Kernel Audit session ID of the session if auditing is + available. + + Type encodes the session type. It's one of unspecified (for + cron PAM sessions and suchlike), tty (for text logins) or + x11/mir/wayland (for graphical logins). + + Class encodes the session class. It's one of user (for + normal user sessions), greeter (for display manager pseudo-sessions), or + lock-screen (for display lock screens). + + Active is a boolean that is true if the session is active, i.e. currently in the + foreground. This field is semi-redundant due to State. + + State encodes the session state and one of online, + active, or closing. See + sd_session_get_state3 + for more information about the states. + + IdleHint, IdleSinceHint, and + IdleSinceHintMonotonic encapsulate the idle hint state of this session, similarly to + how the respective properties on the manager object do it for the whole system. + + LockedHint shows the locked hint state of this session, as set by the + SetLockedHint() method described above. + + + + + Examples + + + Introspect <interfacename>org.freedesktop.login1.Manager</interfacename> on the bus + + $ gdbus introspect --system --dest org.freedesktop.login1 \ + --object-path /org/freedesktop/login1 + + + + + Introspect <interfacename>org.freedesktop.login1.Seat</interfacename> on the bus + + $ gdbus introspect --system --dest org.freedesktop.login1 \ + --object-path /org/freedesktop/login1/seat/seat0 + + + + + Introspect <interfacename>org.freedesktop.login1.User</interfacename> on the bus + + $ gdbus introspect --system --dest org.freedesktop.login1 \ + --object-path /org/freedesktop/login1/user/_1000 + + + + + Introspect <interfacename>org.freedesktop.login1.Session</interfacename> on the bus + + $ gdbus introspect --system --dest org.freedesktop.login1 \ + --object-path /org/freedesktop/login1/session/45 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.machine1.xml b/man/org.freedesktop.machine1.xml new file mode 100644 index 000000000..a54fa040b --- /dev/null +++ b/man/org.freedesktop.machine1.xml @@ -0,0 +1,643 @@ + + + + + + + org.freedesktop.machine1 + systemd + + + + org.freedesktop.machine1 + 5 + + + + org.freedesktop.machine1 + The D-Bus interface of systemd-machined + + + + Introduction + + + systemd-machined.service8 + is a system service that keeps track of locally running virtual machines and containers. + This page describes the D-Bus interface. + + + + The Manager Object + + The service exposes the following interfaces on the Manager object on the bus: + + +node /org/freedesktop/machine1 { + interface org.freedesktop.machine1.Manager { + methods: + GetMachine(in s name, + out o machine); + GetImage(in s name, + out o image); + GetMachineByPID(in u pid, + out o machine); + ListMachines(out a(ssso) machines); + ListImages(out a(ssbttto) images); + CreateMachine(in s name, + in ay id, + in s service, + in s class, + in u leader, + in s root_directory, + in a(sv) scope_properties, + out o path); + CreateMachineWithNetwork(in s name, + in ay id, + in s service, + in s class, + in u leader, + in s root_directory, + in ai ifindices, + in a(sv) scope_properties, + out o path); + RegisterMachine(in s name, + in ay id, + in s service, + in s class, + in u leader, + in s root_directory, + out o path); + RegisterMachineWithNetwork(in s name, + in ay id, + in s service, + in s class, + in u leader, + in s root_directory, + in ai ifindices, + out o path); + UnregisterMachine(in s name); + TerminateMachine(in s id); + KillMachine(in s name, + in s who, + in i signal); + GetMachineAddresses(in s name, + out a(iay) addresses); + GetMachineOSRelease(in s name, + out a{ss} fields); + OpenMachinePTY(in s name, + out h pty, + out s pty_path); + OpenMachineLogin(in s name, + out h pty, + out s pty_path); + OpenMachineShell(in s name, + in s user, + in s path, + in as args, + in as environment, + out h pty, + out s pty_path); + BindMountMachine(in s name, + in s source, + in s destination, + in b read_only, + in b mkdir); + CopyFromMachine(in s name, + in s source, + in s destination); + CopyToMachine(in s name, + in s source, + in s destination); + OpenMachineRootDirectory(in s name, + out h fd); + GetMachineUIDShift(in s name, + out u shift); + RemoveImage(in s name); + RenameImage(in s name, + in s new_name); + CloneImage(in s name, + in s new_name, + in b read_only); + MarkImageReadOnly(in s name, + in b read_only); + GetImageHostname(in s name, + out s hostname); + GetImageMachineID(in s name, + out ay id); + GetImageMachineInfo(in s name, + out a{ss} machine_info); + GetImageOSRelease(in s name, + out a{ss} os_release); + SetPoolLimit(in t size); + SetImageLimit(in s name, + in t size); + CleanPool(in s mode, + out a(st) images); + MapFromMachineUser(in s name, + in u uid_inner, + out u uid_outer); + MapToMachineUser(in u uid_outer, + out s machine_name, + out o machine_path, + out u uid_inner); + MapFromMachineGroup(in s name, + in u gid_inner, + out u gid_outer); + MapToMachineGroup(in u gid_outer, + out s machine_name, + out o machine_path, + out u gid_inner); + signals: + MachineNew(s machine, + o path); + MachineRemoved(s machine, + o path); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s PoolPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t PoolUsage = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t PoolLimit = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + GetMachine() may be used to get the machine object path for the machine with + the specified name. Similarly, GetMachineByPID() gets the machine object the + specified PID belongs to if there is any. + + GetImage() may be used to get the image object path of the image with the + specified name. + + ListMachines() returns an array of all currently registered machines. The + structures in the array consist of the following fields: machine name, machine class, an identifier for + the service that registered the machine and the machine object path. + + ListImages() returns an array of all currently known images. The + structures in the array consist of the following fields: image name, type, read-only flag, creation + time, modification time, current disk space, and image object path. + + CreateMachine() may be used to register a new virtual machine or container + with systemd-machined, creating a scope unit for it. It accepts the following arguments: a + machine name chosen by the registrar, an optional UUID as a 32 byte array, a string that identifies the + service that registers the machine, a class string, the PID of the leader process of the machine, an + optional root directory of the container, and an array of additional properties to use for the scope + registration. The virtual machine name must be suitable as a hostname, and hence should follow the usual + DNS hostname rules, as well as the Linux hostname restrictions. Specifically, only 7 bit ASCII is + permitted, a maximum length of 64 characters is enforced, only characters from the set + a-zA-Z0-9-_. are allowed, the name may not begin with a dot, and it may not contain + two dots immediately following each other. Container and VM managers should ideally use the hostname + used internally in the machine for this parameter. This recommendation is made in order to make the + machine name naturally resolvable using + nss-mymachines8. If + a container manager needs to embed characters outside of the indicated range, escaping is required, + possibly using _ as the escape character. Another (somewhat natural) option would be + to utilize Internet IDNA encoding. The UUID is passed as a 32 byte array or, if no suitable UUID is + available, an empty array (zero length) or zeroed out array shall be passed. The UUID should identify + the virtual machine/container uniquely and should ideally be the same UUID that + /etc/machine-id in the VM/container is initialized from. The service string can be + free-form, but it is recommended to pass a short lowercase identifier like + systemd-nspawn, libvirt-lxc or similar. The class string should + be either container or vm indicating whether the machine to + register is of the respective class. The leader PID should be the host PID of the init process of the + container or the encapsulating process of the VM. If the root directory of the container is known and + available in the host's hierarchy, it should be passed. Otherwise, pass the empty string instead. Finally, the + scope properties are passed as array in the same way as to PID1's + StartTransientUnit() method. Calling this method will internally register a transient scope + unit for the calling client (utilizing the passed scope_properties) and move the leader PID into + it. The method returns an object path for the registered machine object that implements the + org.freedesktop.machine1.Machine interface (see below). Also see the + New Control Group + Interfaces for details about scope units and how to alter resource control settings on the + created machine at runtime. + + RegisterMachine() is similar to CreateMachine(). + However, it only registers a machine and does not create a scope unit for it. Instead, the caller's unit is + registered. We recommend to only use this method for container or VM managers that are run + multiple times, one instance for each container/VM they manage, and are invoked as system + services. + + CreateMachineWithNetwork() and + RegisterMachineWithNetwork() are similar to CreateMachine() + and RegisterMachine() but take an extra argument: an array of network interface + indices that point towards the virtual machine or container. The interface indices should reference one + or more network interfaces on the host that can be used to communicate with the guest. Commonly, the + passed interface index refers to the host side of a "veth" link (in case of containers), a + "tun"/"tap" link (in case of VMs), or the host side of a bridge interface that bridges access to the + VM/container interfaces. Specifying this information is useful to enable support for link-local IPv6 + communication to the machines since the scope field of sockaddr_in6 can be initialized by the + specified ifindex. + nss-mymachines8 + makes use of this information. + + KillMachine() sends a UNIX signal to the machine's processes. As its arguments, it takes a + machine name (as originally passed to CreateMachine() or returned by + ListMachines()), an identifier that specifies what precisely to send the signal to (either + leader or all), and a numeric UNIX signal integer. + + TerminateMachine() terminates a virtual machine, killing its processes. It + takes a machine name as its only argument. + + GetMachineAddresses() retrieves the IP addresses of a container. This method + returns an array of pairs consisting of an address family specifier (AF_INET or + AF_INET6) and a byte array containing the addresses. This is only supported for + containers that make use of network namespacing. + + GetMachineOSRelease() retrieves the OS release information of a + container. This method returns an array of key value pairs read from the + os-release5 file in + the container and is useful to identify the operating system used in a container. + + OpenMachinePTY() allocates a pseudo TTY in the container and returns a file + descriptor and its path. This is equivalent to transitioning into the container and invoking + posix_openpt3. + + + OpenMachineLogin() allocates a pseudo TTY in the container and ensures that + a getty login prompt of the container is running on the other end. It returns the file descriptor of + the PTY and the PTY path. This is useful for acquiring a pty with a login prompt from the + container. + + OpenMachineShell() allocates a pseudo TTY in the container, as the specified + user, and invokes the executable at the specified path with a list of arguments (starting from + argv[0]) and an environment block. It then returns the file descriptor of the PTY and the PTY + path. + + BindMountMachine() bind mounts a file or directory from the host into the + container. Its arguments consist of a machine name, the source directory on the host, the destination directory in the + container, and two booleans, one indicating whether the bind mount shall be + read-only, the other indicating whether the destination mount point shall be created first, if it is + missing. + + CopyFromMachine() copies files or directories from a container into the + host. It takes a container name, a source directory in the container and a destination directory on the + host as arguments. CopyToMachine() does the opposite and copies files from a source + directory on the host into a destination directory in the container. + + RemoveImage() removes the image with the specified name. + + RenameImage() renames the specified image. + + CloneImage() clones the specified image under a new name. It also takes a + boolean argument indicating whether the resulting image shall be read-only or not. + + MarkImageReadOnly() toggles the read-only flag of an image. + + SetPoolLimit() sets an overall quota limit on the pool of images. + + SetImageLimit() sets a per-image quota limit. + + MapFromMachineUser(), MapToMachineUser(), + MapFromMachineGroup(), and MapToMachineGroup() may be used to map + UIDs/GIDs from the host user namespace to a container user namespace or vice versa. + + + + Signals + + MachineNew and MachineRemoved are sent whenever a new + machine is registered or removed. These signals carry the machine name and the object path to the corresponding + org.freedesktop.machine1.Machine interface (see below). + + + + Properties + + PoolPath specifies the file system path where images are written to. + + PoolUsage specifies the current usage size of the image pool in bytes. + + PoolLimit specifies the size limit of the image pool in bytes. + + + + + Machine Objects + + +node /org/freedesktop/machine1/machine/rawhide { + interface org.freedesktop.machine1.Machine { + methods: + Terminate(); + Kill(in s who, + in i signal); + GetAddresses(out a(iay) addresses); + GetOSRelease(out a{ss} fields); + GetUIDShift(out u shift); + OpenPTY(out h pty, + out s pty_path); + OpenLogin(out h pty, + out s pty_path); + OpenShell(in s user, + in s path, + in as args, + in as environment, + out h pty, + out s pty_path); + BindMount(in s source, + in s destination, + in b read_only, + in b mkdir); + CopyFrom(in s source, + in s destination); + CopyTo(in s source, + in s destination); + OpenRootDirectory(out h fd); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Name = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay Id = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t Timestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Service = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Unit = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Leader = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Class = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ai NetworkInterfaces = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s State = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Terminate() and Kill() terminate/kill the machine. These methods + take the same arguments as TerminateMachine() and + KillMachine() on the Manager interface, respectively. + + GetAddresses() and GetOSRelease() get the IP address and OS + release information from the machine. These methods take the same arguments as + GetMachineAddresses() and GetMachineOSRelease() of the + Manager interface, respectively. + + + + Properties + + Name is the machine name as it was passed in during registration with + CreateMachine() on the manager object. + + Id is the machine UUID. + + Timestamp and TimestampMonotonic are the realtime and + monotonic timestamps when the virtual machines where created in microseconds since the epoch. + + Service contains a short string identifying the registering service as passed + in during registration of the machine. + + Unit is the systemd scope or service unit name for the machine. + + Leader is the PID of the leader process of the machine. + + Class is the class of the machine and is either the string "vm" (for real VMs + based on virtualized hardware) or "container" (for light-weight userspace virtualization sharing the + same kernel as the host). + + RootDirectory is the root directory of the container if it is known and + applicable or the empty string. + + NetworkInterfaces contains an array of network interface indices that point + towards the container, the VM or the host. For details about this information see the description of + CreateMachineWithNetwork() above. + + State is the state of the machine and is one of opening, + running, or closing. Note that the state machine is not considered + part of the API and states might be removed or added without this being considered API breakage. + + + + + + Examples + + + Introspect <interfacename>org.freedesktop.machine1.Manager</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.machine1 \ + --object-path /org/freedesktop/machine1 + + + + + Introspect <interfacename>org.freedesktop.machine1.Machine</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.machine1 \ + --object-path /org/freedesktop/machine1/machine/rawhide + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml new file mode 100644 index 000000000..96e22c655 --- /dev/null +++ b/man/org.freedesktop.resolve1.xml @@ -0,0 +1,810 @@ + + + +%entities; +]> + + + + + org.freedesktop.resolve1 + systemd + + + + org.freedesktop.resolve1 + 5 + + + + org.freedesktop.resolve1 + The D-Bus interface of systemd-resolved + + + + Introduction + + + systemd-resolved.service8 + is a system service that provides hostname resolution and caching using DNS, LLMNR, and mDNS. It also + does DNSSEC validation. This page describes the resolve semantics and the D-Bus interface. + + This page contains an API reference only. If you are looking for a longer explanation how to use + this API, please consult + + Writing Network Configuration Managers and + Writing Resolver + Clients. + + + + + The Manager Object + + The service exposes the following interfaces on the Manager object on the bus: + + +node /org/freedesktop/resolve1 { + interface org.freedesktop.resolve1.Manager { + methods: + ResolveHostname(in i ifindex, + in s name, + in i family, + in t flags, + out a(iiay) addresses, + out s canonical, + out t flags); + ResolveAddress(in i ifindex, + in i family, + in ay address, + in t flags, + out a(is) names, + out t flags); + ResolveRecord(in i ifindex, + in s name, + in q class, + in q type, + in t flags, + out a(iqqay) records, + out t flags); + ResolveService(in i ifindex, + in s name, + in s type, + in s domain, + in i family, + in t flags, + out a(qqqsa(iiay)s) srv_data, + out aay txt_data, + out s canonical_name, + out s canonical_type, + out s canonical_domain, + out t flags); + GetLink(in i ifindex, + out o path); + SetLinkDNS(in i ifindex, + in a(iay) addresses); + SetLinkDomains(in i ifindex, + in a(sb) domains); + SetLinkDefaultRoute(in i ifindex, + in b enable); + SetLinkLLMNR(in i ifindex, + in s mode); + SetLinkMulticastDNS(in i ifindex, + in s mode); + SetLinkDNSOverTLS(in i ifindex, + in s mode); + SetLinkDNSSEC(in i ifindex, + in s mode); + SetLinkDNSSECNegativeTrustAnchors(in i ifindex, + in as names); + RevertLink(in i ifindex); + RegisterService(in s name, + in s name_template, + in s type, + in q service_port, + in q service_priority, + in q service_weight, + in aa{say} txt_datas, + out o service_path); + UnregisterService(in o service_path); + ResetStatistics(); + FlushCaches(); + ResetServerFeatures(); + properties: + readonly s LLMNRHostname = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s LLMNR = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s MulticastDNS = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DNSOverTLS = '...'; + readonly a(iiay) DNS = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(iiay) FallbackDNS = [...]; + readonly (iiay) CurrentDNSServer = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(isb) Domains = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly (tt) TransactionStatistics = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly (ttt) CacheStatistics = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DNSSEC = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly (tttt) DNSSECStatistics = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b DNSSECSupported = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DNSSECNegativeTrustAnchors = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DNSStubListener = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + ResolveHostname() takes a hostname and resolves it to one or more IP addresses. + As parameters it takes the Linux network interface index to execute the query on, or 0 if it may be + done on any suitable interface. The name parameter specifies the hostname to + resolve. Note that if required, IDNA conversion is applied to this name unless it is resolved via LLMNR or MulticastDNS. The family parameter + limits the results to a specific address family. It may be AF_INET, + AF_INET6 or AF_UNSPEC. If AF_UNSPEC is specified (recommended), both kinds are retrieved, subject + to local network configuration (i.e. if no local, routable IPv6 address is found, no IPv6 address is + retrieved; and similarly for IPv4). A 64-bit flags field may be used to alter the + behaviour of the resolver operation (see below). The method returns an array of address records. Each + address record consists of the interface index the address belongs to, an address family as well as a + byte array with the actual IP address data (which either has 4 or 16 elements, depending on the address + family). The returned address family will be one of AF_INET or + AF_INET6. For IPv6, the returned address interface index should be used to + initialize the .sin6_scope_id field of a struct sockaddr_in6 instance to permit + support for resolution to link-local IP addresses. The address array is followed by the canonical name + of the host, which may or may not be identical to the resolved hostname. Finally, a 64-bit + flags field is returned that is defined similarly to the flags + field that was passed in, but contains information about the resolved data (see below). If the hostname + passed in is an IPv4 or IPv6 address formatted as string, it is parsed, and the result is returned. In + this case, no network communication is done. + + ResolveAddress() executes the reverse operation: it takes an IP address and + acquires one or more hostnames for it. As parameters it takes the interface index to execute the query + on, or 0 if all suitable interfaces are OK. The family + parameter indicates the address family of the IP address to resolve. It may be either + AF_INET or AF_INET6. The address parameter + takes the raw IP address data (as either a 4 or 16 byte array). The flags input + parameter may be used to alter the resolver operation (see below). The method returns an array of name + records, each consisting of an interface index and a hostname. The flags output + field contains additional information about the resolver operation (see below). + + ResolveRecord() takes a DNS resource record (RR) type, class and name, and + retrieves the full resource record set (RRset), including the RDATA, for it. As parameter it takes the + Linux network interface index to execute the query on, or 0 if it may be done on + any suitable interface. The name parameter specifies the RR domain name to look up + (no IDNA conversion is applied), followed by the 16-bit class and type fields (which may be + ANY). Finally, a flags field may be passed in to alter behaviour of the look-up (see + below). On completion, an array of RR items is returned. Each array entry consists of the network interface + index the RR was discovered on, the type and class field of the RR found, and a byte array of the raw + RR discovered. The raw RR data starts with the RR's domain name, in the original casing, followed + by the RR type, class, TTL and RDATA, in the binary format documented in + RFC 1035. For RRs that support name + compression in the payload (such as MX or PTR), the compression is expanded in the returned + data. + + Note that currently, the class field has to be specified as IN or ANY. Specifying a different + class will return an error indicating that look-ups of this kind are unsupported. Similarly, some + special types are not supported either (AXFR, OPT, …). While systemd-resolved parses and validates resource + records of many types, it is crucial that clients using this API understand that the RR data originates + from the network and should be thoroughly validated before use. + + ResolveService() may be used to resolve a DNS SRV service record, as well as the + hostnames referenced in it, and possibly an accompanying DNS-SD TXT record containing additional + service metadata. The primary benefit of using this method over ResolveRecord() + specifying the SRV type is that it will resolve the SRV and TXT RRs as well as the hostnames referenced + in the SRV in a single operation. As parameters it takes a Linux network interface index, a service + name, a service type and a service domain. This method may be invoked in three different modes: + + + To resolve a DNS-SD service, specify the service name (e.g. Lennart's + Files), the service type (e.g. _webdav._tcp) and the domain to search in + (e.g. local) as the three service parameters. The service name must be in UTF-8 + format, and no IDNA conversion is applied to it in this mode (as mandated by the DNS-SD + specifications). However, if necessary, IDNA conversion is applied to the domain parameter. + + + To resolve a plain SRV record, set the service name parameter to the empty string + and set the service type and domain properly. (IDNA conversion is applied to the domain, if + necessary.) + + Alternatively, leave both the service name and type empty and specify the full + domain name of the SRV record (i.e. prefixed with the service type) in the domain parameter. (No IDNA + coversion is applied in this mode.) + + + The family parameter of the ResolveService() method encodes + the desired family of the addresses to resolve (use AF_INET, + AF_INET6, or AF_UNSPEC). If this is enabled (Use the + NO_ADDRESS flag to turn address resolution off, see below). The + flags parameter takes a couple of flags that may be used to alter the resolver + operation. + + On completion, ResolveService() returns an array of SRV record structures. Each + items consisting of the priority, weight and port fields as well as the hostname to contact, as encoded in the SRV + record. Immediately following is an array of the addresses of this hostname, with each item consisting + of the interface index, the address family and the address data in a byte array. This address array is + followed by the canonicalized hostname. After this array of SRV record structures an array of byte + arrays follows that encodes the TXT RR strings, in case DNS-SD look-ups are enabled. The next parameters + are the canonical service name, type and domain. This may or may not be identical to the parameters + passed in. Finally, a flags field is returned that contains information about the + resolver operation performed. + + The ResetStatistics() method resets the various statistics counters that + systemd-resolved maintains to zero. (For details, see the statistics properties below.) + + The GetLink() method takes a network interface index and returns the object + path to the org.freedesktop.resolve1.Link object corresponding to it. + + + The SetLinkDNS() method sets the DNS servers to use on a specific + interface. This method (and the following ones) may be used by network management software to configure + per-interface DNS settings. It takes a network interface index as well as an array of DNS server IP + address records. Each array item consists of an address family (either AF_INET or + AF_INET6), followed by a 4-byte or 16-byte array with the raw address data. This + method is a one-step shortcut for retrieving the Link object for a network interface using + GetLink() (see above) and then invoking the SetDNS() method + (see below) on it. + + + Network management software integrating with systemd-resolved should + call this method (and the five below) after the interface appeared in the kernel (and thus after a + network interface index has been assigned), but before the network interfaces is activated + (IFF_UP set) so that all settings take effect during the full time the network + interface is up. It is safe to alter settings while the interface is up, however. Use + RevertLink() (described below) to reset all per-interface settings. + + The SetLinkDomains() method sets the search and routing domains to use on a + specific network interface for DNS look-ups. It takes a network interface index and an array of domains, + each with a boolean parameter indicating whether the specified domain shall be used as a search domain + (false), or just as a routing domain (true). Search domains are used for qualifying single-label names into + FQDN when looking up hostnames, as well as for making routing decisions on which interface to send + queries ending in the domain to. Routing domains are only used for routing decisions and not used for single-label + name qualification. Pass the search domains in the order they should be used. + + The SetLinkLLMNR() method enables or disables LLMNR support on a specific + network interface. It takes a network interface index as well as a string that may either be empty or one of + yes, no or resolve. If empty, the systemd-wide + default LLMNR setting is used. If yes, LLMNR is used for resolution of single-label + names and the local hostname is registered on all local LANs for LLMNR resolution by peers. If + no, LLMNR is turned off fully on this interface. If resolve, LLMNR + is only enabled for resolving names, but the local hostname is not registered for other peers to + use. + + Similarly, the SetLinkMulticastDNS() method enables or disables MulticastDNS + support on a specific interface. It takes the same parameters as SetLinkLLMNR() + described above. + + The SetLinkDNSSEC() method enables or disables DNSSEC validation on a + specific network interface. It takes a network interface index as well as a string that may either be + empty or one of yes, no, or allow-downgrade. When + empty, the system-wide default DNSSEC setting is used. If yes, full DNSSEC validation + is done for all look-ups. If the selected DNS server does not support DNSSEC, look-ups will fail if this + mode is used. If no, DNSSEC validation is fully disabled. If + allow-downgrade, DNSSEC validation is enabled, but is turned off automatically if the + selected server does not support it (thus opening up behaviour to downgrade attacks). Note that DNSSEC + only applies to traditional DNS, not to LLMNR or MulticastDNS. + + The SetLinkDNSSECNegativeTrustAnchors() method may be used to configure DNSSEC + Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a + list of domains as arguments. + + The RevertLink() method may be used to revert all per-link settings done with + the six methods described above to the defaults again. + + + The Flags Parameter + + The four methods above accept and return a 64-bit flags value. In most cases passing 0 is sufficient + and recommended. However, the following flags are defined to alter the look-up: + + +#define SD_RESOLVED_DNS (UINT64_C(1) << 0) +#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) +#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) +#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3) +#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4) +#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) +#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) +#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) +#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8) +#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9) + + + On input, the first five flags control the protocols to use for the look-up. They refer to + classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via + IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the + look-up will be done via all suitable protocols for the specific look-up. Note that these flags + operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, systemd-resolved + will only route look-ups within the .local TLD to MulticastDNS (plus some reverse look-up address + domains), and single-label names to LLMNR (plus some reverse address lookup domains). It will route + neither of these to Unicast DNS servers. Also, it will do LLMNR and Multicast DNS only on interfaces + suitable for multicast. + + On output, these five flags indicate which protocol was used to execute the operation, and hence + where the data was found. + + The primary use cases for these five flags are follow-up look-ups based on DNS data retrieved + earlier. In this case it is often a good idea to limit the follow-up look-up to the protocol that was + used to discover the first DNS result. + + The NO_CNAME flag controls whether CNAME/DNAME resource records shall be followed during the + look-up. This flag is only available at input, none of the functions will return it on output. If a + CNAME/DNAME RR is discovered while resolving a hostname, an error is returned instead. By default, + when the flag is off, CNAME/DNAME RRs are followed. + + The NO_TXT and NO_ADDRESS flags only influence operation of the + ResolveService() method. They are only defined for input, not output. If + NO_TXT set, the DNS-SD TXT RR look-up is not done in the same operation. If NO_ADDRESS is specified, + the hostnames discovered are not implicitly translated to their addresses. + + The NO_SEARCH flag turns off the search domain logic. It is only defined for input in + ResolveHostname(). When specified, single-label hostnames are not qualified + using defined search domains, if any are configured. Note that ResolveRecord() + will never qualify single-label domain names using search domains. Also note that + multi-label hostnames are never subject to search list expansion. + + The AUTHENTICATED bit is defined only in the output flags of the four functions. If set, the + returned data has been fully authenticated. Specifically, this bit is set for all DNSSEC-protected data + for which a full trust chain may be established to a trusted domain anchor. It is also set for locally + synthesized data, such as localhost or data from + /etc/hosts. Moreover, it is set for all LLMNR or mDNS RRs which originate from the + local host. Applications that require authenticated RR data for operation should check this flag before + trusting the data. Note that systemd-resolved will never return invalidated data, hence this flag + simply allows to discern the cases where data is known to be trustable, or where there is proof that + the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server + does not support authenticating data). + + + + + + Properties + + LLMNRHostname contains the hostname currently exposed on the network via + LLMNR. It usually follows the system hostname as may be queried via + gethostname3, + but may differ if a conflict is detected on the network. + + DNS contains an array of all DNS servers currently used by + systemd-resolved. It contains similar information as the DNS server data written to + /run/systemd/resolve/resolv.conf. Each structure in the array consists of a numeric network interface + index, an address family, and a byte array containing the DNS server address (either 4 bytes in length + for IPv4 or 16 bytes in lengths for IPv6). The array contains DNS servers configured system-wide, + including those possibly read from a foreign /etc/resolv.conf or the + DNS= setting in /etc/systemd/resolved.conf, as well as + per-interface DNS server information either retrieved from + systemd-networkd8, + or configured by external software via SetLinkDNS() (see above). The network + interface index will be 0 for the system-wide configured services and non-zero for the per-link + servers. + + Similarly, the Domains property contains an array of all search and + routing domains currently used by systemd-resolved. Each entry consists of a network interface index (again, 0 + encodes system-wide entries), the actual domain name, and whether the entry is used only for routing + (true) or for both routing and searching (false). + + The TransactionStatistics property contains information about the number of + transactions systemd-resolved has processed. It contains a pair of unsigned 64-bit counters, the first + containing the number of currently ongoing transactions, the second the number of total transactions + systemd-resolved is processing or has processed. The latter value may be reset using the + ResetStatistics() method described above. Note that the number of transactions does + not directly map to the number of issued resolver bus method calls. While simple look-ups usually require a + single transaction only, more complex look-ups might result in more, for example when CNAMEs or DNSSEC + are in use. + + The CacheStatistics property contains information about the executed cache + operations so far. It exposes three 64-bit counters: the first being the total number of current cache + entries (both positive and negative), the second the number of cache hits, and the third the number of + cache misses. The latter counters may be reset using ResetStatistics() (see + above). + + The DNSSECStatistics property contains information about the DNSSEC + validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus, + and indeterminate DNSSEC validations so far. The counters are increased for each validated RRset, and + each non-existance proof. The secure counter is increased for each operation that successfully verified + a signed reply, the insecure counter is increased for each operation that successfully verified that an + unsigned reply is rightfully unsigned. The bogus counter is increased for each operation where the + validation did not check out and the data is likely to have been tempered with. Finally the + indeterminate counter is increased for each operation which did not complete because the necessary keys + could not be acquired or the cryptographic algorithms were unknown. + + The DNSSECSupported boolean property reports whether DNSSEC is enabled and + the selected DNS servers support it. It combines information about system-wide and per-link DNS + settings (see below), and only reports true if DNSSEC is enabled and supported on every interface for + which DNS is configured and for the system-wide settings if there are any. Note that systemd-resolved assumes + DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported + value may initially be true, until the first transactions are executed. + + The LogLevel property shows the (maximum) log level of the manager, with the + same values as the option described in + systemd1. + + + + + Link Object + + +node /org/freedesktop/resolve1/link/_1 { + interface org.freedesktop.resolve1.Link { + methods: + SetDNS(in a(iay) addresses); + SetDomains(in a(sb) domains); + SetDefaultRoute(in b enable); + SetLLMNR(in s mode); + SetMulticastDNS(in s mode); + SetDNSOverTLS(in s mode); + SetDNSSEC(in s mode); + SetDNSSECNegativeTrustAnchors(in as names); + Revert(); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ScopesMask = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iay) DNS = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly (iay) CurrentDNSServer = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(sb) Domains = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b DefaultRoute = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s LLMNR = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s MulticastDNS = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DNSOverTLS = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DNSSEC = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DNSSECNegativeTrustAnchors = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b DNSSECSupported = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For each Linux network interface a "Link" object is created which exposes per-link DNS + configuration and state. Use GetLink() on the Manager interface to retrieve the + object path for a link object given the network interface index (see above). + + + Methods + + The various methods exposed by the Link interface are equivalent to their similarly named + counterparts on the Manager interface. e.g. SetDNS() on the Link object maps to + SetLinkDNS() on the Manager object, the main difference being that the later + expects an interface index to be specified. Invoking the methods on the Manager interface has the + benefit of reducing roundtrips, as it is not necessary to first request the Link object path via + GetLink() before invoking the methods. For further details on these methods see + the Manager documentation above. + + + + Properties + + ScopesMask defines which resolver scopes are currently active on this + interface. This 64-bit unsigned integer field is a bit mask consisting of a subset of the bits of the + flags parameter describe above. Specifically, it may have the DNS, LLMNR and MDNS bits (the latter in + IPv4 and IPv6 flavours) set. Each individual bit is set when the protocol applies to a specific + interface and is enabled for it. It is unset otherwise. Specifically, a multicast-capable interface in + the "UP" state with an IP address is suitable for LLMNR or MulticastDNS, and any interface that is UP and + has an IP address is suitable for DNS. Note the relationship of the bits exposed here with the LLMNR + and MulticastDNS properties also exposed on the Link interface. The latter expose what is *configured* + to be used on the interface, the former expose what is actually used on the interface, taking into + account the abilities of the interface. + + DNSSECSupported exposes a boolean field that indicates whether DNSSEC is + currently configured and in use on the interface. Note that if DNSSEC is enabled on an interface, it is + assumed available until it is detected that the configured server does not actually support it. Thus, + this property may initially report that DNSSEC is supported on an interface. + + The other properties reflect the state of the various configuration settings for the link which + may be set with the various methods calls such as SetDNS() or SetLLMNR(). + + + + + Common Errors + + Many bus methods systemd-resolved exposes (in particular the resolver methods such + as ResolveHostname() on the Manager interface) may return + some of the following errors: + + + org.freedesktop.resolve1.NoNameServers + No suitable DNS servers were found to resolve a request. + + + org.freedesktop.resolve1.InvalidReply + A response from the selected DNS server was not understood. + + + org.freedesktop.resolve1.NoSuchRR + The requested name exists, but there is no resource record of the requested type for + it. (This is the DNS NODATA case). + + org.freedesktop.resolve1.CNameLoop + The look-up failed because a CNAME or DNAME loop was detected. + + + org.freedesktop.resolve1.Aborted + The look-up was aborted because the selected protocol became unavailable while the + operation was ongoing. + + + org.freedesktop.resolve1.NoSuchService + A service look-up was successful, but the SRV record reported that the service is not + available. + + org.freedesktop.resolve1.DnssecFailed + The acquired response did not pass DNSSEC validation. + + + org.freedesktop.resolve1.NoTrustAnchor + No chain of trust could be established for the response to a configured DNSSEC trust + anchor. + + + org.freedesktop.resolve1.ResourceRecordTypeUnsupported + The requested resource record type is not supported on the selected DNS servers. This + error is generated for example when an RRSIG record is requested from a DNS server that does not + support DNSSEC. + + + + org.freedesktop.resolve1.NoSuchLink + No network interface with the specified network interface index exists. + + + org.freedesktop.resolve1.LinkBusy + The requested configuration change could not be made because + systemd-networkd8, + already took possession of the interface and supplied configuration data for it. + + + org.freedesktop.resolve1.NetworkDown + The requested look-up failed because the system is currently not connected to any + suitable network. + + org.freedesktop.resolve1.DnsError.NXDOMAIN + org.freedesktop.resolve1.DnsError.REFUSED + ... + The look-up failed with a DNS return code reporting a failure. The error names used as + suffixes here are defined in by IANA in + DNS RCODEs. + + + + + + + Examples + + + Introspect <interfacename>org.freedesktop.resolve1.Manager</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.resolve1 \ + --object-path /org/freedesktop/resolve1 + + + + + Introspect <interfacename>org.freedesktop.resolve1.Link</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.resolve1 \ + --object-path /org/freedesktop/resolve1/link/_11 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml new file mode 100644 index 000000000..6b16ae16d --- /dev/null +++ b/man/org.freedesktop.systemd1.xml @@ -0,0 +1,9400 @@ + + + + + + + org.freedesktop.systemd1 + systemd + + + + org.freedesktop.systemd1 + 5 + + + + org.freedesktop.systemd1 + The D-Bus interface of systemd + + + + Introduction + + + systemd1 and its + auxiliary daemons expose a number of APIs over D-Bus. This page only describes the various APIs exposed by the + system and service manager itself. It does not cover the auxiliary daemons. + + + The service manager exposes a number of objects on the bus: one + Manager object as a central entry point for clients along with individual objects + for each unit and for each queued job. The unit objects each implement a generic + Unit interface as well as a type-specific interface. For example, service units + implement both org.freedesktop.systemd1.Unit and + org.freedesktop.system1.Service. The manager object can list + unit and job objects or directly convert a unit name or job id into a bus path of the corresponding + D-Bus object. + + Properties exposing time values are usually encoded in microseconds (usec) on the bus, even if + their corresponding settings in the unit files are in seconds. + + In contrast to most of the other services of the systemd suite, PID 1 does not use + polkit + for controlling access to privileged operations, but relies exclusively on the low-level D-Bus policy + language. (This is done in order to avoid a cyclic dependency between polkit and systemd/PID 1.) This + means that sensitive operations exposed by PID 1 on the bus are generally not available to unprivileged + processes directly. However, some operations (such as shutdown/reboot/suspend) are made available through the D-Bus + API of logind, see + org.freedesktop.login15. + + + + + The Manager Object + + The main entry point object is available on the fixed + /org/freedesktop/systemd1 object path: + + +node /org/freedesktop/systemd1 { + interface org.freedesktop.systemd1.Manager { + methods: + GetUnit(in s name, + out o unit); + GetUnitByPID(in u pid, + out o unit); + GetUnitByInvocationID(in ay invocation_id, + out o unit); + GetUnitByControlGroup(in s cgroup, + out o unit); + LoadUnit(in s name, + out o unit); + StartUnit(in s name, + in s mode, + out o job); + StartUnitReplace(in s old_unit, + in s new_unit, + in s mode, + out o job); + StopUnit(in s name, + in s mode, + out o job); + ReloadUnit(in s name, + in s mode, + out o job); + RestartUnit(in s name, + in s mode, + out o job); + TryRestartUnit(in s name, + in s mode, + out o job); + ReloadOrRestartUnit(in s name, + in s mode, + out o job); + ReloadOrTryRestartUnit(in s name, + in s mode, + out o job); + EnqueueUnitJob(in s name, + in s job_type, + in s job_mode, + out u job_id, + out o job_path, + out s unit_id, + out o unit_path, + out s job_type, + out a(uosos) affected_jobs); + KillUnit(in s name, + in s whom, + in i signal); + CleanUnit(in s name, + in as mask); + FreezeUnit(in s name); + ThawUnit(in s name); + ResetFailedUnit(in s name); + SetUnitProperties(in s name, + in b runtime, + in a(sv) properties); + RefUnit(in s name); + UnrefUnit(in s name); + StartTransientUnit(in s name, + in s mode, + in a(sv) properties, + in a(sa(sv)) aux, + out o job); + GetUnitProcesses(in s name, + out a(sus) processes); + AttachProcessesToUnit(in s unit_name, + in s subcgroup, + in au pids); + AbandonScope(in s name); + GetJob(in u id, + out o job); + GetJobAfter(in u id, + out a(usssoo) jobs); + GetJobBefore(in u id, + out a(usssoo) jobs); + CancelJob(in u id); + ClearJobs(); + ResetFailed(); + ListUnits(out a(ssssssouso) units); + ListUnitsFiltered(in as states, + out a(ssssssouso) units); + ListUnitsByPatterns(in as states, + in as patterns, + out a(ssssssouso) units); + ListUnitsByNames(in as names, + out a(ssssssouso) units); + ListJobs(out a(usssoo) jobs); + Subscribe(); + Unsubscribe(); + Dump(out s output); + DumpByFileDescriptor(out h fd); + Reload(); + Reexecute(); + Exit(); + Reboot(); + PowerOff(); + Halt(); + KExec(); + SwitchRoot(in s new_root, + in s init); + SetEnvironment(in as assignments); + UnsetEnvironment(in as names); + UnsetAndSetEnvironment(in as names, + in as assignments); + ListUnitFiles(out a(ss) unit_files); + ListUnitFilesByPatterns(in as states, + in as patterns, + out a(ss) unit_files); + GetUnitFileState(in s file, + out s state); + EnableUnitFiles(in as files, + in b runtime, + in b force, + out b carries_install_info, + out a(sss) changes); + DisableUnitFiles(in as files, + in b runtime, + out a(sss) changes); + ReenableUnitFiles(in as files, + in b runtime, + in b force, + out b carries_install_info, + out a(sss) changes); + LinkUnitFiles(in as files, + in b runtime, + in b force, + out a(sss) changes); + PresetUnitFiles(in as files, + in b runtime, + in b force, + out b carries_install_info, + out a(sss) changes); + PresetUnitFilesWithMode(in as files, + in s mode, + in b runtime, + in b force, + out b carries_install_info, + out a(sss) changes); + MaskUnitFiles(in as files, + in b runtime, + in b force, + out a(sss) changes); + UnmaskUnitFiles(in as files, + in b runtime, + out a(sss) changes); + RevertUnitFiles(in as files, + out a(sss) changes); + SetDefaultTarget(in s name, + in b force, + out a(sss) changes); + GetDefaultTarget(out s name); + PresetAllUnitFiles(in s mode, + in b runtime, + in b force, + out a(sss) changes); + AddDependencyUnitFiles(in as files, + in s target, + in s type, + in b runtime, + in b force, + out a(sss) changes); + GetUnitFileLinks(in s name, + in b runtime, + out as links); + SetExitCode(in y number); + LookupDynamicUserByName(in s name, + out u uid); + LookupDynamicUserByUID(in u uid, + out s name); + GetDynamicUsers(out a(us) users); + signals: + UnitNew(s id, + o unit); + UnitRemoved(s id, + o unit); + JobNew(u id, + o job, + s unit); + JobRemoved(u id, + o job, + s unit, + s result); + StartupFinished(t firmware, + t loader, + t kernel, + t initrd, + t userspace, + t total); + UnitFilesChanged(); + Reloading(b active); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Version = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Features = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Virtualization = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Architecture = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Tainted = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t FirmwareTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t FirmwareTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LoaderTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LoaderTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t KernelTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t KernelTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UserspaceTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UserspaceTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t FinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t FinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SecurityStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SecurityStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SecurityFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SecurityFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t GeneratorsStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t GeneratorsStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t GeneratorsFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t GeneratorsFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UnitsLoadStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UnitsLoadStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UnitsLoadFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t UnitsLoadFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDSecurityStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDSecurityStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDSecurityFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDSecurityFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDGeneratorsStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDGeneratorsStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDGeneratorsFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDGeneratorsFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDUnitsLoadStartTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDUnitsLoadStartTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDUnitsLoadFinishTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t InitRDUnitsLoadFinishTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s LogLevel = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s LogTarget = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NNames = ...; + readonly u NFailedUnits = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NJobs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NInstalledJobs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NFailedJobs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly d Progress = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ConfirmSpawn = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b ShowStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as UnitPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s DefaultStandardOutput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s DefaultStandardError = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite t RuntimeWatchdogUSec = ...; + @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") + readwrite t KExecWatchdogUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite b ServiceWatchdogs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s SystemState = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly y ExitCode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultTimerAccuracyUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultTimeoutStartUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultTimeoutStopUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultTimeoutAbortUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultRestartUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultStartLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u DefaultStartLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DefaultCPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DefaultBlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DefaultMemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DefaultTasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitCPU = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitCPUSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitFSIZE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitFSIZESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitDATA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitDATASoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitSTACK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitSTACKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitCORE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitCORESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRSS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRSSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNOFILE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNOFILESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitAS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitASSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNPROC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNPROCSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitMEMLOCK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitMEMLOCKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitLOCKS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitLOCKSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitSIGPENDING = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitSIGPENDINGSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitMSGQUEUE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitMSGQUEUESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNICE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitNICESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRTPRIO = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRTPRIOSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRTTIME = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DefaultLimitRTTIMESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultTasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimerSlackNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s DefaultOOMPolicy = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Note that many of the methods exist twice: once on the Manager + object and once on the respective unit objects. This is to optimize access times so that methods that + belong to unit objects do not have to be called with a resolved unit path, but can be called with only + the unit id, too. + + GetUnit() may be used to get the unit object path for a unit name. It takes + the unit name and returns the object path. If a unit has not been loaded yet by this name this method + will fail. + + GetUnitByPID() may be used to get the unit object path of the unit a process + ID belongs to. It takes a UNIX PID and returns the object path. The PID must refer to an existing system process. + + LoadUnit() is similar to GetUnit() but will load the + unit from disk if possible. + + StartUnit() enqueues a start job and possibly depending jobs. It takes the unit + to activate and a mode string as arguments. The mode needs to be one of replace, + fail, isolate, ignore-dependencies, or + ignore-requirements. If replace, the method will start the unit and + its dependencies, possibly replacing already queued jobs that conflict with it. If + fail, the method will start the unit and its dependencies, but will fail if this would + change an already queued job. If isolate, the method will start the unit in question + and terminate all units that aren't dependencies of it. If ignore-dependencies, it + will start a unit but ignore all its dependencies. If ignore-requirements, it will + start a unit but only ignore the requirement dependencies. It is not recommended to make use of the + latter two options. On completion, this method returns the newly created job object. + + StartUnitReplace() is similar to StartUnit() but + replaces a job that is queued for one unit by a job for another unit. + + StopUnit() is similar to StartUnit() but stops the + specified unit rather than starting it. Note that the isolate mode is invalid for this + method. + + ReloadUnit(), RestartUnit(), + TryRestartUnit(), ReloadOrRestartUnit(), or + ReloadOrTryRestartUnit() may be used to restart and/or reload a unit. These methods take + similar arguments as StartUnit(). Reloading is done only if the unit is already + running and fails otherwise. If a service is restarted that isn't running, it will be started unless + the "Try" flavor is used in which case a service that isn't running is not affected by the restart. The + "ReloadOrRestart" flavors attempt a reload if the unit supports it and use a restart otherwise. + + KillUnit() may be used to kill (i.e. send a signal to) all processes of a + unit. It takes the unit name, an enum who and a UNIX + signal number to send. The who enum is one of + main, control or all. If + main, only the main process of the unit is killed. If control, only + the control process of the unit is killed. If all, all processes are killed. A + control process is for example a process that is configured via + ExecStop= and is spawned in parallel to the main daemon process in order to shut it + down. + + GetJob() returns the job object path for a specific job, identified by its + id. + + CancelJob() cancels a specific job identified by its numeric ID. This + operation is also available in the Cancel() method of Job objects (see below) and + exists primarily to reduce the necessary round trips to execute this operation. Note that this will not + have any effect on jobs whose execution has already begun. + + ClearJobs() flushes the job queue, removing all jobs that are still + queued. Note that this does not have any effect on jobs whose execution has already begun. It only + flushes jobs that are queued and have not yet begun execution. + + ResetFailedUnit() resets the "failed" state of a specific unit. + + ResetFailed() resets the "failed" state of all units. + + ListUnits() returns an array of all currently loaded units. Note that + units may be known by multiple names at the same name, and hence there might be more unit names loaded + than actual units behind them. The array consists of structures with the following elements: + + The primary unit name as string + + The human readable description string + + The load state (i.e. whether the unit file has been loaded + successfully) + + The active state (i.e. whether the unit is currently started or + not) + + The sub state (a more fine-grained version of the active state that is specific to + the unit type, which the active state is not) + + A unit that is being followed in its state by this unit, if there is any, otherwise + the empty string. + + The unit object path + + If there is a job queued for the job unit, the numeric job id, 0 + otherwise + + The job type as string + + The job object path + + + ListJobs() returns an array with all currently queued jobs. Returns an array + consisting of structures with the following elements: + + The numeric job id + + The primary unit name for this job + + The job type as string + + The job state as string + + The job object path + + The unit object path + + + Subscribe() enables most bus signals to be sent out. Clients which are + interested in signals need to call this method. Signals are only sent out if at least one client + invoked this method. Unsubscribe() reverts the signal subscription that + Subscribe() implements. It is not necessary to invoke + Unsubscribe() as clients are tracked. Signals are no longer sent out as soon as + all clients which previously asked for Subscribe() either closed their connection + to the bus or invoked Unsubscribe(). + + Reload() may be invoked to reload all unit files. + + Reexecute() may be invoked to reexecute the main manager process. It will + serialize its state, reexecute, and deserizalize the state again. This is useful for upgrades and is a + more comprehensive version of Reload(). + + Exit() may be invoked to ask the manager to exit. This is not available for + the system manager and is useful only for user session managers. + + Reboot(), PowerOff(), Halt(), or + KExec() may be used to ask for immediate reboot, powering down, halt or kexec + based reboot of the system. Note that this does not shut down any services and immediately transitions + into the reboot process. These functions are normally only called as the last step of shutdown and should + not be called directly. To shut down the machine, it is generally a better idea to invoke + Reboot() or PowerOff() on the + systemd-logind manager object; see + org.freedesktop.login15 + for more information. + + SwitchRoot() may be used to transition to a new root directory. This is + intended to be used by initial RAM disks. The method takes two arguments: the new root directory (which + needs to be specified) and an init binary path (which may be left empty, in which case it is + automatically searched for). The state of the system manager will be serialized before the + transition. After the transition, the manager binary on the main system is invoked and replaces the old + PID 1. All state will then be deserialized. + + SetEnvironment() may be used to alter the environment block that is passed + to all spawned processes. It takes a string array of environment variable assignments. Any previously set + environment variables will be overridden. + + UnsetEnvironment() may be used to unset environment variables. It takes a + string array of environment variable names. All variables specified will be unset (if they have been + set previously) and no longer be passed to all spawned processes. This method has no effect for variables + that were previously not set, but will not fail in that case. + + UnsetAndSetEnvironment() is a combination of + UnsetEnvironment() and SetEnvironment(). It takes two + lists. The first list contains variables to unset, the second one contains assignments to set. If a + variable is listed in both, the variable is set after this method returns, i.e. the set list overrides the + unset list. + + ListUnitFiles() returns an array of unit names and their enablement + status. Note that ListUnit() returns a list of units currently loaded into memory, + while ListUnitFiles() returns a list of unit files that were + found on disk. Note that while most units are read directly from a unit file with the same name, some + units are not backed by files and some files (templates) cannot directly be loaded as units but need + to be instantiated instead. + + GetUnitFileState() returns the current enablement status of a specific unit + file. + + EnableUnitFiles() may be used to enable one or more units in the system (by + creating symlinks to them in /etc or /run). It takes a list + of unit files to enable (either just file names or full absolute paths if the unit files are residing + outside the usual unit search paths) and two booleans: the first controls whether the unit shall be + enabled for runtime only (true, /run), or persistently (false, + /etc). The second one controls whether symlinks pointing to other units shall be + replaced if necessary. This method returns one boolean and an array of the changes made. The boolean + signals whether the unit files contained any enablement information (i.e. an [Install]) section. The + changes array consists of structures with three strings: the type of the change (one of + symlink or unlink), the file name of the symlink and the + destination of the symlink. Note that most of the following calls return a changes list in the same + format. + + Similarly, DisableUnitFiles() disables one or more units in the system, + i.e. removes all symlinks to them in /etc and /run. + + Similarly, ReenableUnitFiles() applies the changes to one or more units that + would result from disabling and enabling the unit quickly one after the other in an atomic + fashion. This is useful to apply updated [Install] information contained in unit files. + + Similarly, LinkUnitFiles() links unit files (that are located outside of the + usual unit search paths) into the unit search path. + + Similarly, PresetUnitFiles() enables/disables one or more unit files + according to the preset policy. See + systemd.preset7 for more + information. + + Similarly, MaskUnitFiles() masks unit files and + UnmaskUnitFiles() unmasks them again. + + SetDefaultTarget() changes the default.target link. See + bootup7 for more + information. + + GetDefaultTarget() retrieves the name of the unit to which + default.target is aliased. + + SetUnitProperties() may be used to modify certain unit properties at + runtime. Not all properties may be changed at runtime, but many resource management settings (primarily + those listed in + systemd.resource-control5) + may. The changes are applied instantly and stored on disk for future boots, unless + runtime is true, in which case the settings only apply until the next + reboot. name is the name of the unit to modify. properties are + the settings to set, encoded as an array of property name and value pairs. Note that this is not a + dictionary! Also note that when setting array properties with this method usually results in appending to + the pre-configured array. To reset the configured arrays, set the property to an empty array first and + then append to it. + + StartTransientUnit() may be used to create and start a transient unit which + will be released as soon as it is not running or referenced anymore or the system is + rebooted. name is the unit name including its suffix and must be + unique. mode is the same as in StartUnit(), + properties contains properties of the unit, specified like in + SetUnitProperties(). aux is currently unused and should be + passed as an empty array. See the + New Control Group + Interface for more information how to make use of this functionality for resource control + purposes. + + + + Signals + + Note that most signals are sent out only after Subscribe() has been invoked + by at least one client. Make sure to invoke this method when subscribing to these signals! + + UnitNew() and UnitRemoved() are sent out each time a + new unit is loaded or unloaded. Note that this has little to do with whether a unit is available on + disk or not, and simply reflects the units that are currently loaded into memory. The signals take two + parameters: the primary unit name and the object path. + + JobNew() and JobRemoved() are sent out each time a new + job is queued or dequeued. Both signals take the numeric job ID, the bus path and the primary unit name + for this job as arguments. JobRemoved() also includes a result string which is one + of done, canceled, timeout, + failed, dependency, or + skipped. done indicates successful execution of a + job. canceled indicates that a job has been canceled (via + CancelJob() above) before it finished execution (this doesn't necessarily mean + though that the job operation is actually cancelled too, see above). timeout + indicates that the job timeout was reached. failed indicates that the job + failed. dependency indicates that a job this job depended on failed and the job hence + was removed as well. skipped indicates that a job was skipped because + it didn't apply to the unit's current state. + + StartupFinished() is sent out when startup finishes. It carries six + microsecond timespan values, each indicating how much boot time has been spent in the firmware (if + known), in the boot loader (if known), in the kernel initialization phase, in the initrd (if known), in + userspace and in total. These values may also be calculated from the + FirmwareTimestampMonotonic, LoaderTimestampMonotonic, + InitRDTimestampMonotonic, UserspaceTimestampMonotonic, and + FinishTimestampMonotonic properties (see below). + + UnitFilesChanged() is sent out each time the list of enabled or masked unit + files on disk have changed. + + Reloading() is sent out immediately before a daemon reload is done (with the + boolean parameter set to True) and after a daemon reload is completed (with the boolean parameter set + to False). This may be used by UIs to optimize UI updates. + + + + Properties + + Most properties simply reflect the respective options in + /etc/systemd/system.conf and the kernel command line. + + The others: + + Version encodes the version string of the running systemd instance. Note that + the version string is purely informational. It should not be parsed and one may not assume the version to + be formatted in any particular way. We take the liberty to change the versioning scheme at any time and + it is not part of the public API. + + Features encodes the features that have been enabled and disabled for this + build. Enabled options are prefixed with +, disabled options with -. + + Tainted encodes a couple of taint flags as a colon-separated list. When + systemd detects it is running on a system with certain problems, it will set an appropriate taint + flag. Taints may be used to lower the chance of bogus bug reports. The following taints are currently + known: split-usr, mtab-not-symlink, + cgroups-missing, local-hwclock. split-usr is + set if /usr is not pre-mounted when systemd is first invoked. See + + Booting Without /usr is Broken + for details why this is bad. mtab-not-symlink indicates that + /etc/mtab is not a symlink to /proc/self/mounts as + required. cgroups-missing indicates that control groups have not been enabled in the + kernel. local-hwclock indicates that the local RTC is configured to be in local time + rather than UTC. + + FirmwareTimestamp, FirmwareTimestampMonotonic, + LoaderTimestamp, LoaderTimestampMonotonic, + KernelTimestamp, KernelTimestampMonotonic, + InitRDTimestamp, InitRDTimestampMonotonic, + UserspaceTimestamp, UserspaceTimestampMonotonic, + FinishTimestamp, and FinishTimestampMonotonic encode + CLOCK_REALTIME and CLOCK_MONOTONIC microsecond timestamps + taken when the firmware first began execution, when the boot loader first began execution, when the + kernel first began execution, when the initrd first began execution, when the main systemd instance + began execution and finally, when all queued startup jobs finished execution. These values are useful + for determining boot-time performance. Note that as monotonic time begins with the kernel startup, the + KernelTimestampMonotonic timestamp will always be 0 and + FirmwareTimestampMonotonic and LoaderTimestampMonotonic are to + be read as negative values. Also, not all fields are always available, depending on the used firmware, + boot loader or initrd implementation. In these cases the respective pairs of timestamps are both 0, + indicating that no data is available. + + Similarly, the SecurityStartTimestamp, + GeneratorsStartTimestamp and LoadUnitTimestamp (as well as their + monotonic and stop counterparts) expose performance data for uploading the security policies to the + kernel (such as the SELinux, IMA, or SMACK policies), for running the generator tools and for loading + the unit files. + + NNames encodes how many unit names are currently known. This only includes + names of units that are currently loaded and can be more than the amount of actually loaded units since + units may have more than one name. + + NJobs encodes how many jobs are currently queued. + + NInstalledJobs encodes how many jobs have ever been queued in total. + + NFailedJobs encodes how many jobs have ever failed in total. + + Progress encodes boot progress as a floating point value between 0.0 and + 1.0. This value begins at 0.0 at early-boot and ends at 1.0 when boot is finished and is based on the + number of executed and queued jobs. After startup, this field is always 1.0 indicating a finished + boot. + + Environment encodes the environment block passed to all executed services. It + may be altered with bus calls such as SetEnvironment() (see above). + + UnitPath encodes the currently active unit file search path. It is an array of + file system paths encoded as strings. + + Virtualization contains a short ID string describing the virtualization + technology the system runs in. On bare-metal hardware this is the empty string. Otherwise, it contains + an identifier such as kvm, vmware and so on. For a full list of + IDs see + systemd-detect-virt1. + Note that only the "innermost" virtualization technology is exported here. This detects both + full-machine virtualizations (VMs) and shared-kernel virtualization (containers). + + Architecture contains a short ID string describing the architecture the + systemd instance is running on. This follows the same vocabulary as + ConditionArchitectures=. + + ControlGroup contains the root control group path of this system manager. Note + that the root path is encoded as the empty string here (not as /!), so that it can be + appended to /sys/fs/cgroup/systemd easily. This value will be set to the empty + string for the host instance and some other string for container instances. + + + + Security + + Read access is generally granted to all clients. Additionally, for unprivileged clients, some + operations are allowed through the polkit privilege system. Operations which modify unit state + (StartUnit(), StopUnit(), KillUnit(), + RestartUnit() and similar, SetProperty()) require + org.freedesktop.systemd1.manage-units. Operations which modify unit file + enablement state (EnableUnitFiles(), DisableUnitFiles(), + ReenableUnitFiles(), LinkUnitFiles(), + PresetUnitFiles, MaskUnitFiles, and similar) require + org.freedesktop.systemd1.manage-unit-files. Operations which modify the + exported environment (SetEnvironment(), UnsetEnvironment(), + UnsetAndSetEnvironment()) require + org.freedesktop.systemd1.set-environment. Reload() + and Reexecute() require + org.freedesktop.systemd1.reload-daemon. + + + + + + Unit Objects + + +node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + interface org.freedesktop.systemd1.Unit { + methods: + Start(in s mode, + out o job); + Stop(in s mode, + out o job); + Reload(in s mode, + out o job); + Restart(in s mode, + out o job); + TryRestart(in s mode, + out o job); + ReloadOrRestart(in s mode, + out o job); + ReloadOrTryRestart(in s mode, + out o job); + EnqueueJob(in s job_type, + in s job_mode, + out u job_id, + out o job_path, + out s unit_id, + out o unit_path, + out s job_type, + out a(uosos) affected_jobs); + Kill(in s whom, + in i signal); + ResetFailed(); + SetProperties(in b runtime, + in a(sv) properties); + Ref(); + Unref(); + Clean(in as mask); + Freeze(); + Thaw(); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Id = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Names = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Following = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Requires = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Requisite = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Wants = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as BindsTo = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PartOf = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RequiredBy = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RequisiteOf = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as WantedBy = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as BoundBy = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConsistsOf = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Conflicts = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConflictedBy = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Before = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as After = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as OnFailure = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Triggers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as TriggeredBy = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PropagatesReloadTo = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReloadPropagatedFrom = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as JoinsNamespaceOf = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RequiresMountsFor = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Documentation = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Description = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s LoadState = '...'; + readonly s ActiveState = '...'; + readonly s FreezerState = '...'; + readonly s SubState = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s FragmentPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SourcePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as DropInPaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s UnitFileState = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s UnitFilePreset = '...'; + readonly t StateChangeTimestamp = ...; + readonly t StateChangeTimestampMonotonic = ...; + readonly t InactiveExitTimestamp = ...; + readonly t InactiveExitTimestampMonotonic = ...; + readonly t ActiveEnterTimestamp = ...; + readonly t ActiveEnterTimestampMonotonic = ...; + readonly t ActiveExitTimestamp = ...; + readonly t ActiveExitTimestampMonotonic = ...; + readonly t InactiveEnterTimestamp = ...; + readonly t InactiveEnterTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanStart = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanStop = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanReload = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanIsolate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as CanClean = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CanFreeze = ...; + readonly (uo) Job = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b StopWhenUnneeded = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RefuseManualStart = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RefuseManualStop = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b AllowIsolate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DefaultDependencies = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s OnFailureJobMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreOnIsolate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NeedDaemonReload = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t JobTimeoutUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t JobRunningTimeoutUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s JobTimeoutAction = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s JobTimeoutRebootArgument = '...'; + readonly b ConditionResult = ...; + readonly b AssertResult = ...; + readonly t ConditionTimestamp = ...; + readonly t ConditionTimestampMonotonic = ...; + readonly t AssertTimestamp = ...; + readonly t AssertTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sbbsi) Conditions = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sbbsi) Asserts = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (ss) LoadError = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Transient = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Perpetual = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t StartLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u StartLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StartLimitAction = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s FailureAction = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FailureActionExitStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SuccessAction = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SuccessActionExitStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RebootArgument = '...'; + readonly ay InvocationID = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s CollectMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as Refs = ['...', ...]; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Start(), Stop(), Reload(), + Restart(), TryRestart(), + ReloadOrRestart(), ReloadOrTryRestart(), + Kill(), ResetFailed(), and + SetProperties() implement the same operation as the respective methods on the + Manager object (see above). However, these methods operate on the unit + object and hence do not take a unit name parameter. Invoking the methods directly on the Manager + object has the advantage of not requiring a GetUnit() call to get the unit object + for a specific unit name. Calling the methods on the Manager object is hence a round trip + optimization. + + + + Properties + + Id contains the primary name of the unit. + + Names contains all names of the unit, including the primary name that is also + exposed in Id. + + Following either contains the empty string or contains the name of another + unit that this unit follows in state. This is used for some device units which reflect the unit state + machine of another unit, and which other unit this is might possibly change. + + Requires, RequiresOverridable, + Requisite, RequisiteOverridable, Wants, + BindsTo, RequiredBy, RequiredByOverridable, + WantedBy, BoundBy, Conflicts, + ConflictedBy, Before, After, + OnFailure, Triggers, TriggeredBy, + PropagatesReloadTo, and RequiresMountsFor contain arrays which encode + the dependencies and their inverse dependencies (where this applies) as configured in the unit file or + determined automatically. + + Description contains the human readable description string for the + unit. + + SourcePath contains the path to a configuration file this unit is + automatically generated from in case it is not a native unit (in which case it contains the empty + string). For example, all mount units generated from /etc/fstab have this field + set to /etc/fstab. + + Documentation contains a string array with URLs of documentation for this + unit. + + LoadState contains a state value that reflects whether the configuration file + of this unit has been loaded. The following states are currently defined: loaded, + error, and masked. loaded indicates that the + configuration was successfully loaded. error indicates that the configuration failed + to load. The LoadError field (see below) contains information about the cause of + this failure. masked indicates that the unit is currently masked out (i.e. symlinked + to /dev/null or empty). Note that the LoadState is fully + orthogonal to the ActiveState (see below) as units without valid loaded + configuration might be active (because configuration might have been reloaded at a time where a unit + was already active). + + ActiveState contains a state value that reflects whether the unit is currently + active or not. The following states are currently defined: active, + reloading, inactive, failed, + activating, and deactivating. active indicates + that unit is active (obviously...). reloading indicates that the unit is active and + currently reloading its configuration. inactive indicates that it is inactive and + the previous run was successful or no previous run has taken place yet. failed + indicates that it is inactive and the previous run was not successful (more information about the + reason for this is available on the unit type specific interfaces, for example for services in the + Result property, see below). activating indicates that the unit + has previously been inactive but is currently in the process of entering an active state. Conversely + deactivating indicates that the unit is currently in the process of + deactivation. + + SubState encodes states of the same state machine that + ActiveState covers, but knows more fine-grained states that are + unit-type-specific. Where ActiveState only covers six high-level states, + SubState covers possibly many more low-level unit-type-specific states that are + mapped to the six high-level states. Note that multiple low-level states might map to the same + high-level state, but not vice versa. Not all high-level states have low-level counterparts on all unit + types. At this point the low-level states are not documented here, and are more likely to be extended + later on than the common high-level states explained above. + + FragmentPath contains the unit file path this unit was read from, if there is + one (if not, it contains the empty string). + + UnitFileState encodes the install state of the unit file of + FragmentPath. It currently knows the following states: enabled, + enabled-runtime, linked, linked-runtime, + masked, masked-runtime, static, + disabled, and invalid. enabled indicates that a + unit file is permanently enabled. enable-runtime indicates the unit file is only + temporarily enabled and will no longer be enabled after a reboot (that means, it is enabled via + /run symlinks, rather than /etc). linked + indicates that a unit is linked into /etc permanently. linked-runtime + indicates that a unit is linked into /run temporarily (until the next + reboot). masked indicates that the unit file is masked permanently. + masked-runtime indicates that it is masked in /run temporarily + (until the next reboot). static indicates that the unit is statically enabled, i.e. + always enabled and doesn't need to be enabled explicitly. invalid indicates that it + could not be determined whether the unit file is enabled. + + InactiveExitTimestamp, InactiveExitTimestampMonotonic, + ActiveEnterTimestamp, ActiveEnterTimestampMonotonic, + ActiveExitTimestamp, ActiveExitTimestampMonotonic, + InactiveEnterTimestamp, and InactiveEnterTimestampMonotonic + contain CLOCK_REALTIME and CLOCK_MONOTONIC 64-bit microsecond + timestamps of the last time a unit left the inactive state, entered the active state, exited the active + state, or entered an inactive state. These are the points in time where the unit transitioned + inactive/failedactivating, + activatingactive, active → + deactivating, and finally deactivating → + inactive/failed. The fields are 0 in case such a transition has + not yet been recorded on this boot. + + CanStart, CanStop, and CanReload encode + as booleans whether the unit supports the start, stop or reload operations. Even if a unit supports + such an operation, the client might not necessary have the necessary privileges to execute them. + + CanIsolate encodes as a boolean whether the unit may be started in isolation + mode. + + Job encodes the job ID and job object path of the job currently scheduled or + executed for this unit, if there is any. If no job is scheduled or executed, the job id field will be + 0. + + StopWhenUnneeded, RefuseManualStart, + RefuseManualStop, AllowIsolate, + DefaultDependencies, OnFailureIsolate, + IgnoreOnIsolate, IgnoreOnSnapshot map directly to the + corresponding configuration booleans in the unit file. + + DefaultControlGroup contains the main control group of this unit as a + string. This refers to a group in systemd's own name=systemd hierarchy, which + systemd uses to watch and manipulate the unit and all its processes. + + NeedDaemonReload is a boolean that indicates whether the configuration file + this unit is loaded from (i.e. FragmentPath or SourcePath) has + changed since the configuration was read and hence whether a configuration reload is + recommended. + + JobTimeoutUSec maps directly to the corresponding configuration setting in the + unit file. + + ConditionTimestamp and ConditionTimestampMonotonic contain + the CLOCK_REALTIME/CLOCK_MONOTONIC microsecond timestamps of + the last time the configured conditions of the unit have been checked or 0 if they have never been + checked. Conditions are checked when a unit is requested to start. + + ConditionResult contains the condition result of the last time the configured + conditions of this unit were checked. + + Conditions contains all configured conditions of the unit. For each condition, + five fields are given: condition type (e.g. ConditionPathExists), whether the + condition is a trigger condition, whether the condition is reversed, the right hand side of the + condition (e.g. the path in case of ConditionPathExists), and the status. The status + can be 0, in which case the condition hasn't been checked yet, a positive value, in which case the + condition passed, or a negative value, in which case the condition failed. Currently only 0, +1, and -1 + are used, but additional values may be used in the future, retaining the meaning of + zero/positive/negative values. + + LoadError contains a pair of strings. If the unit failed to load (as encoded + in LoadState, see above), then this will include a D-Bus error pair consisting of + the error ID and an explanatory human readable string of what happened. If it loaded successfully, this + will be a pair of empty strings. + + Transient contains a boolean that indicates whether the unit was created as a + transient unit (i.e. via CreateTransientUnit() on the manager object). + + + + Security + + Similarly to methods on the Manager object, read-only access is + allowed for everyone. All operations are allowed for clients with the + CAP_SYS_ADMIN capability or when the + org.freedesktop.systemd1.manage-units privilege is granted by + polkit. + + + + + Service Unit Objects + + All service unit objects implement the + org.freedesktop.systemd1.Service interface (described here) in addition to + the generic org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + interface org.freedesktop.systemd1.Service { + methods: + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Type = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Restart = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PIDFile = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NotifyAccess = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestartUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutStartUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutStopUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TimeoutAbortUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RuntimeMaxUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t WatchdogUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t WatchdogTimestamp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t WatchdogTimestampMonotonic = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RootDirectoryStartOnly = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemainAfterExit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b GuessMainPID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (aiai) RestartPreventExitStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (aiai) RestartForceExitStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (aiai) SuccessExitStatus = ...; + readonly u MainPID = ...; + readonly u ControlPID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s BusName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u FileDescriptorStoreMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NFileDescriptorStore = ...; + readonly s StatusText = '...'; + readonly i StatusErrno = ...; + readonly s Result = '...'; + readonly s ReloadResult = '...'; + readonly s CleanResult = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s USBFunctionDescriptors = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s USBFunctionStrings = '...'; + readonly u UID = ...; + readonly u GID = ...; + readonly u NRestarts = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s OOMPolicy = '...'; + readonly t ExecMainStartTimestamp = ...; + readonly t ExecMainStartTimestampMonotonic = ...; + readonly t ExecMainExitTimestamp = ...; + readonly t ExecMainExitTimestampMonotonic = ...; + readonly u ExecMainPID = ...; + readonly i ExecMainCode = ...; + readonly i ExecMainStatus = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecCondition = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecConditionEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStartPre = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecStartPreEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStart = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecStartEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStartPost = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecStartPostEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecReload = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecReloadEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStop = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecStopEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStopPost = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasasttttuii) ExecStopPostEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(sb) EnvironmentFiles = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PassEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as UnsetEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UMask = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPU = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPUSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATASoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitAS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitASSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROCSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDING = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDINGSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIO = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIOSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIME = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIMESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s WorkingDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootImage = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i OOMScoreAdjust = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CoredumpFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Nice = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingClass = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay CPUAffinity = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUAffinityFromNUMA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i NUMAPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay NUMAMask = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimerSlackNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUSchedulingResetOnFork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NonBlocking = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay StandardInputData = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardError = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardErrorFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TTYPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYReset = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVHangup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVTDisallocate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SyslogIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SyslogLevelPrefix = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogLevel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogFacility = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i LogLevelMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LogRateLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogRateLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly aay LogExtraFields = [[...], ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s LogNamespace = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SecureBits = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CapabilityBoundingSet = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t AmbientCapabilities = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s User = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Group = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DynamicUser = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SupplementaryGroups = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PAMName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadWritePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadOnlyPaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as InaccessiblePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t MountFlags = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateTmp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateDevices = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectClock = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelTunables = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelModules = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelLogs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectControlGroups = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateNetwork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateUsers = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateMounts = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectHome = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectSystem = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SameProcessGroup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SELinuxContext = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) AppArmorProfile = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SmackProcessLabel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreSIGPIPE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NoNewPrivileges = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) SystemCallFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SystemCallArchitectures = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SystemCallErrorNumber = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Personality = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b LockPersonality = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) RestrictAddressFamilies = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RuntimeDirectoryPreserve = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u RuntimeDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RuntimeDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u StateDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as StateDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u CacheDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as CacheDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogsDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as LogsDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u ConfigurationDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConfigurationDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutCleanUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MemoryDenyWriteExecute = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictRealtime = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictSUIDSGID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestrictNamespaces = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindReadOnlyPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) TemporaryFileSystem = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MountAPIVFS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KeyringMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectHostname = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NetworkNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i KillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i RestartKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FinalKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGKILL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGHUP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i WatchdogSignal = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most properties of the Service interface map directly to the corresponding settings in service + unit files. For the sake of brevity, here's a list of all exceptions only: + + WatchdogTimestamp and WatchdogTimestampMonotonic contain + CLOCK_REALTIME/CLOCK_MONOTONIC microsecond timestamps of the + last watchdog ping received from the service, or 0 if none was ever received. + + ExecStartPre, ExecStart, ExecStartPost, + ExecReload, ExecStop, and ExecStop are arrays + of structures where each struct contains: the binary path to execute; an array with all arguments to + pass to the executed command, starting with argument 0; a boolean whether it should be considered a + failure if the process exits uncleanly; two pairs of + CLOCK_REALTIME/CLOCK_MONOTONIC microsecond timestamps when + the process began and finished running the last time, or 0 if it never ran or never finished running; + the PID of the process, or 0 if it has not run yet; the exit code and status of the last run. This + field hence maps more or less to the corresponding setting in the service unit file but is augmented + with runtime data. + + LimitCPU (and related properties) map more or less directly to the + corresponding settings in the service unit files except that if they aren't set, their value is + 18446744073709551615 (i.e. -1). + + Capabilities contains the configured capabilities, as formatted with + cap_to_text3. + + + SecureBits, CapabilityBoundingSet, + MountFlags also correspond to the configured settings of the unit files, but + instead of being formatted as strings, they are encoded as the actual binary flags they are. + + + ExecMainStartTimestamp, ExecMainStartTimestampMonotonic, + ExecMainExitTimestamp, ExecMainExitTimestampMonotonic, + ExecMainPID, ExecMainCode, ExecMainStatus + contain information about the main process of the service as far as it is known. This is often the same + runtime information that is stored in ExecStart. However, it deviates for + Type=forking services where the main process of the service is not forked off + systemd directly. These fields either contain information of the last run of the process or of the + current running process. + + MainPID and ControlPID contain the main and control PID of + the service. The main PID is the current main PID of the service and is 0 when the service currently + has no main PID. The control PID is the PID of the current start/stop/reload process running and is 0 + if no such process is currently running. That means that ExecMainPID and + MainPID differ in the way that the latter immediately reflects whether a main + process is currently running while the latter possible contains information collected from the last run + even if the process is no longer around. + + StatusText contains the status text passed to the service manager via a call + to + sd_notify3. + This may be used by services to inform the service manager about its internal state with a nice + explanatory string. + + Result encodes the execution result of the last run of the service. It is + useful to determine the reason a service failed if it is in the failed state (see + ActiveState above). The following values are currently known: + success is set if the unit didn't fail. resources indicates that + not enough resources were available to fork off and execute the service + processes. timeout indicates that a timeout occurred while executing a service + operation. exit-code indicates that a service process exited with an unclean exit + code. signal indicates that a service process exited with an uncaught + signal. core-dump indicates that a service process exited uncleanly and dumped + core. watchdog indicates that a service did not send out watchdog ping messages + often enough. start-limit indicates that a service has been started too frequently + in a specific time frame (as configured in StartLimitInterval, + StartLimitBurst). + + ControlGroup indicates the control group path the processes of this service + unit are placed in. + + + + + Socket Unit Objects + + +node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + interface org.freedesktop.systemd1.Socket { + methods: + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s BindIPv6Only = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Backlog = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s BindToDevice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SocketUser = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SocketGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u SocketMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u DirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Accept = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Writable = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b KeepAlive = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t KeepAliveTimeUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t KeepAliveIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u KeepAliveProbes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t DeferAcceptUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NoDelay = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Priority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t ReceiveBuffer = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t SendBuffer = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IPTOS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IPTTL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t PipeSize = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b FreeBind = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Transparent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Broadcast = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PassCredentials = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PassSecurity = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveOnStop = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) Listen = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Symlinks = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Mark = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u MaxConnections = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u MaxConnectionsPerSource = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly x MessageQueueMaxMessages = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly x MessageQueueMessageSize = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TCPCongestion = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ReusePort = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SmackLabel = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SmackLabelIPIn = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SmackLabelIPOut = '...'; + readonly u ControlPID = ...; + readonly s Result = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NConnections = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NAccepted = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NRefused = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s FileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SocketProtocol = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TriggerLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u TriggerLimitBurst = ...; + readonly u UID = ...; + readonly u GID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStartPre = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStartPost = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStopPre = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecStopPost = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(sb) EnvironmentFiles = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PassEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as UnsetEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UMask = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPU = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPUSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATASoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitAS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitASSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROCSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDING = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDINGSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIO = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIOSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIME = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIMESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s WorkingDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootImage = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i OOMScoreAdjust = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CoredumpFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Nice = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingClass = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay CPUAffinity = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUAffinityFromNUMA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i NUMAPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay NUMAMask = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimerSlackNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUSchedulingResetOnFork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NonBlocking = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay StandardInputData = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardError = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardErrorFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TTYPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYReset = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVHangup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVTDisallocate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SyslogIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SyslogLevelPrefix = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogLevel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogFacility = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i LogLevelMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LogRateLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogRateLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly aay LogExtraFields = [[...], ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s LogNamespace = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SecureBits = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CapabilityBoundingSet = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t AmbientCapabilities = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s User = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Group = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DynamicUser = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SupplementaryGroups = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PAMName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadWritePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadOnlyPaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as InaccessiblePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t MountFlags = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateTmp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateDevices = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectClock = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelTunables = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelModules = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelLogs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectControlGroups = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateNetwork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateUsers = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateMounts = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectHome = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectSystem = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SameProcessGroup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SELinuxContext = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) AppArmorProfile = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SmackProcessLabel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreSIGPIPE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NoNewPrivileges = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) SystemCallFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SystemCallArchitectures = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SystemCallErrorNumber = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Personality = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b LockPersonality = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) RestrictAddressFamilies = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RuntimeDirectoryPreserve = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u RuntimeDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RuntimeDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u StateDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as StateDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u CacheDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as CacheDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogsDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as LogsDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u ConfigurationDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConfigurationDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutCleanUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MemoryDenyWriteExecute = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictRealtime = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictSUIDSGID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestrictNamespaces = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindReadOnlyPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) TemporaryFileSystem = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MountAPIVFS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KeyringMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectHostname = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NetworkNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i KillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i RestartKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FinalKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGKILL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGHUP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i WatchdogSignal = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most of the properties map directly to the corresponding settings in socket unit files. As socket + units can include ExecStartPre (and similar) fields which contain information about + processes to execute. They also share most of the fields related to the execution context that Service + objects expose (see above). + + In addition to these properties there are the following: + + NAccepted contains the accumulated number of connections ever accepted on this + socket. This only applies to sockets with Accept set to true, + i.e. those where systemd is responsible for accepted connections. + + Similarly NConnections contains the number of currently open connections on + this socket. It only applies only to socket units with Accept set to + true. + + Result encodes the reason why a socket unit failed if it is in the + failed state (see ActiveState above). The values + success, resources, timeout, + exit-code, signal and core-dump have the same + meaning as they have for the corresponding field of service units (see above). In addition to that, + the value service-failed-permanent indicates that the service of this socket failed + continuously. + + + + + Target Unit Objects + + +node /org/freedesktop/systemd1/unit/basic_2etarget { + interface org.freedesktop.systemd1.Target { + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + Target units have neither type-specific methods nor properties. + + + + + Device Unit Objects + + All device unit objects implement the org.freedesktop.systemd1.Device interface (described here) + in addition to the generic org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/dev_2dttyS0_2edevice { + interface org.freedesktop.systemd1.Device { + properties: + readonly s SysFSPath = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + Properties + + Device units only expose a single type-specific property: + + SysFSPath contains the sysfs path of the kernel device this object corresponds + to. + + + + + Mount Unit Objects + + All mount unit objects implement the org.freedesktop.systemd1.Mount + interface (described here) in addition to the generic + org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/home_2emount { + interface org.freedesktop.systemd1.Mount { + methods: + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Where = '...'; + readonly s What = '...'; + readonly s Options = '...'; + readonly s Type = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutUSec = ...; + readonly u ControlPID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u DirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SloppyOptions = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b LazyUnmount = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ForceUnmount = ...; + readonly s Result = '...'; + readonly u UID = ...; + readonly u GID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecMount = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecUnmount = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecRemount = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(sb) EnvironmentFiles = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PassEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as UnsetEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UMask = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPU = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPUSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATASoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitAS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitASSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROCSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDING = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDINGSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIO = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIOSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIME = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIMESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s WorkingDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootImage = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i OOMScoreAdjust = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CoredumpFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Nice = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingClass = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay CPUAffinity = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUAffinityFromNUMA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i NUMAPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay NUMAMask = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimerSlackNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUSchedulingResetOnFork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NonBlocking = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay StandardInputData = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardError = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardErrorFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TTYPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYReset = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVHangup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVTDisallocate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SyslogIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SyslogLevelPrefix = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogLevel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogFacility = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i LogLevelMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LogRateLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogRateLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly aay LogExtraFields = [[...], ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s LogNamespace = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SecureBits = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CapabilityBoundingSet = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t AmbientCapabilities = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s User = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Group = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DynamicUser = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SupplementaryGroups = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PAMName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadWritePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadOnlyPaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as InaccessiblePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t MountFlags = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateTmp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateDevices = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectClock = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelTunables = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelModules = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelLogs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectControlGroups = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateNetwork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateUsers = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateMounts = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectHome = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectSystem = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SameProcessGroup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SELinuxContext = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) AppArmorProfile = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SmackProcessLabel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreSIGPIPE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NoNewPrivileges = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) SystemCallFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SystemCallArchitectures = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SystemCallErrorNumber = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Personality = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b LockPersonality = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) RestrictAddressFamilies = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RuntimeDirectoryPreserve = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u RuntimeDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RuntimeDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u StateDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as StateDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u CacheDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as CacheDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogsDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as LogsDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u ConfigurationDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConfigurationDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutCleanUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MemoryDenyWriteExecute = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictRealtime = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictSUIDSGID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestrictNamespaces = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindReadOnlyPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) TemporaryFileSystem = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MountAPIVFS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KeyringMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectHostname = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NetworkNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i KillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i RestartKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FinalKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGKILL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGHUP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i WatchdogSignal = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most of the properties map directly to the corresponding settings in mount unit files. As mount + units invoke the /usr/bin/mount command, their bus objects include implicit + ExecMount (and similar) fields which contain information about processes to + execute. They also share most of the fields related to the execution context that Service objects + expose (see above). In addition to these properties there are the following: + + ControlPID contains the PID of the currently running + /usr/bin/mount or /usr/bin/umount command if there is one + running, otherwise 0. + + Result contains a value explaining why a mount unit failed if it failed. It + can take the values success, resources, + timeout, exit-code, signal, or + core-dump which have the identical meaning as the corresponding values of the + corresponding field of service unit objects (see above). + + + + + Automount Unit Objects + + All automount unit objects implement the + org.freedesktop.systemd1.Automount interface (described here) in addition + to the generic org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/proc_2dsys_2dfs_2dbinfmt_5fmisc_2eautomount { + interface org.freedesktop.systemd1.Automount { + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Where = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u DirectoryMode = ...; + readonly s Result = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutIdleUSec = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most of the properties map directly to the corresponding settings in the automount unit + files. + + Result knows the values success and + resources at this time. They have the same meanings as the corresponding values of + the corresponding field of the Service object. + + + + + + Timer Unit Objects + + All timer unit objects implement the org.freedesktop.systemd1.Timer + interface (described here) in addition to the generic + org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer { + interface org.freedesktop.systemd1.Timer { + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Unit = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(stt) TimersMonotonic = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sst) TimersCalendar = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b OnClockChange = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b OnTimezoneChange = ...; + readonly t NextElapseUSecRealtime = ...; + readonly t NextElapseUSecMonotonic = ...; + readonly t LastTriggerUSec = ...; + readonly t LastTriggerUSecMonotonic = ...; + readonly s Result = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t AccuracyUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RandomizedDelayUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b Persistent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b WakeSystem = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemainAfterElapse = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Unit contains the name of the unit to activate when the timer elapses. + + TimersMonotonic contains an array of structs that contain information about + all monotonic timers of this timer unit. The structs contain a string identifying the timer base, which + is one of OnActiveUSec, OnBootUSec, + OnStartupUSec, OnUnitActiveUSec, or + OnUnitInactiveUSec which correspond to the settings of the same names in the timer + unit files; the microsecond offset from this timer base in monotonic time; the next elapsation point on + the CLOCK_MONOTONIC clock, relative to its epoch. + + TimersCalendar contains an array of structs that contain information about all + realtime/calendar timers of this timer unit. The structs contain a string identifying the timer base, + which may only be OnCalendar for now; the calendar specification string; the next + elapsation point on the CLOCK_REALTIME clock, relative to its epoch. + + NextElapseUSecRealtime contains the next elapsation point on the + CLOCK_REALTIME clock in miscroseconds since the epoch, or 0 if this timer event + does not include at least one calendar event. + + Similarly, NextElapseUSecMonotonic contains the next elapsation point on the + CLOCK_MONOTONIC clock in microseconds since the epoch, or 0 if this timer event + does not include at least one monotonic event. + + Result knows the values success and + resources with the same meanings as the matching values of the corresponding + property of the service interface. + + + + + Swap Unit Objects + + All swap unit objects implement the org.freedesktop.systemd1.Swap + interface (described here) in addition to the generic + org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + interface org.freedesktop.systemd1.Swap { + methods: + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + properties: + readonly s What = '...'; + readonly i Priority = ...; + readonly s Options = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutUSec = ...; + readonly u ControlPID = ...; + readonly s Result = '...'; + readonly u UID = ...; + readonly u GID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecActivate = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") + readonly a(sasbttttuii) ExecDeactivate = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(sb) EnvironmentFiles = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as PassEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as UnsetEnvironment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UMask = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPU = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCPUSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitFSIZESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitDATASoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSTACKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitCORESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRSSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNOFILESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitAS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitASSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNPROCSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCK = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMEMLOCKSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitLOCKSSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDING = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitSIGPENDINGSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitMSGQUEUESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitNICESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIO = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTPRIOSoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIME = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LimitRTTIMESoft = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s WorkingDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootDirectory = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RootImage = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i OOMScoreAdjust = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CoredumpFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i Nice = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingClass = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i IOSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i CPUSchedulingPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay CPUAffinity = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUAffinityFromNUMA = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i NUMAPolicy = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay NUMAMask = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimerSlackNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b CPUSchedulingResetOnFork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NonBlocking = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardInputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly ay StandardInputData = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutput = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardOutputFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardError = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s StandardErrorFileDescriptorName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s TTYPath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYReset = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVHangup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b TTYVTDisallocate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogPriority = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s SyslogIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SyslogLevelPrefix = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogLevel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SyslogFacility = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i LogLevelMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t LogRateLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogRateLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly aay LogExtraFields = [[...], ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s LogNamespace = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SecureBits = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t CapabilityBoundingSet = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t AmbientCapabilities = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s User = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Group = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DynamicUser = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RemoveIPC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SupplementaryGroups = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PAMName = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadWritePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ReadOnlyPaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as InaccessiblePaths = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t MountFlags = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateTmp = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateDevices = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectClock = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelTunables = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelModules = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectKernelLogs = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectControlGroups = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateNetwork = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateUsers = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b PrivateMounts = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectHome = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s ProtectSystem = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SameProcessGroup = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpIdentifier = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s UtmpMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SELinuxContext = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) AppArmorProfile = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bs) SmackProcessLabel = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b IgnoreSIGPIPE = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b NoNewPrivileges = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) SystemCallFilter = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SystemCallArchitectures = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i SystemCallErrorNumber = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Personality = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b LockPersonality = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (bas) RestrictAddressFamilies = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s RuntimeDirectoryPreserve = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u RuntimeDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as RuntimeDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u StateDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as StateDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u CacheDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as CacheDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u LogsDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as LogsDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u ConfigurationDirectoryMode = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ConfigurationDirectory = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutCleanUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MemoryDenyWriteExecute = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictRealtime = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b RestrictSUIDSGID = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestrictNamespaces = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ssbt) BindReadOnlyPaths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) TemporaryFileSystem = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MountAPIVFS = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KeyringMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b ProtectHostname = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NetworkNamespacePath = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i KillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i RestartKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FinalKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGKILL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGHUP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i WatchdogSignal = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most of the properties map directly to the corresponding settings in swap unit files. As mount + units invoke the + swapon8 command, + their bus objects include implicit ExecActivate (and similar) fields which contain + information about processes to execute. They also share most of the fields related to the execution + context that Service objects expose (see above). In addition to these properties there are the + following: + + ControlPID contains the PID of the currently running + swapon8 or + swapoff8 + command if there is one running, otherwise 0. + + Result contains a value explaining why a mount unit failed if it failed. It + can take the values success, resources, + timeout, exit-code, signal, or + core-dump which have the identical meanings as the corresponding values of the + corresponding field of service unit objects (see above). + + + + + + Path Unit Objects + + +node /org/freedesktop/systemd1/unit/cups_2epath { + interface org.freedesktop.systemd1.Path { + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s Unit = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) Paths = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b MakeDirectory = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u DirectoryMode = ...; + readonly s Result = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most properties correspond directly with the matching settings in path unit files. + + The others: + + Paths contains an array of structs. Each struct contains the condition to + watch, which can be one of PathExists, PathExistsGlob, + PathChanged, PathModified, or DirectoryNotEmpty + which correspond directly to the matching settings in the path unit files; and the path to watch, + possibly including glob expressions. + + Result contains a result value which can be success or + resources which have the same meaning as the corresponding field of the Service + interface. + + + + + Slice Unit Objects + + All slice unit objects implement the org.freedesktop.systemd1.Slice + interface (described here) in addition to the generic + org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/system_2eslice { + interface org.freedesktop.systemd1.Slice { + methods: + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Properties + + Most properties correspond directly with the matching settings in slice unit files. + + + + + Scope Unit Objects + + All slice unit objects implement the org.freedesktop.systemd1.Scope + interface (described here) in addition to the generic + org.freedesktop.systemd1.Unit interface (see above). + + +node /org/freedesktop/systemd1/unit/session_2d1_2escope { + interface org.freedesktop.systemd1.Scope { + methods: + Abandon(); + GetProcesses(out a(sus) processes); + AttachProcesses(in s subcgroup, + in au pids); + signals: + RequestStop(); + properties: + readonly s Controller = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t TimeoutStopUSec = ...; + readonly s Result = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RuntimeMaxUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s Slice = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ControlGroup = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUUsageNSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay EffectiveMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksCurrent = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPIngressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IPEgressPackets = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOReadOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteBytes = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWriteOperations = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b Delegate = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DelegateControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CPUAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupCPUShares = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPerSecUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t CPUQuotaPeriodUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedCPUs = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly ay AllowedMemoryNodes = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t IOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteBandwidthMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOReadIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IOWriteIOPSMax = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) IODeviceLatencyTargetUSec = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b BlockIOAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t BlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t StartupBlockIOWeight = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIODeviceWeight = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOReadBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(st) BlockIOWriteBandwidth = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b MemoryAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t DefaultMemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMin = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLow = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryHigh = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemorySwapMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t MemoryLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s DevicePolicy = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(ss) DeviceAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b TasksAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TasksMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b IPAccounting = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressAllow = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly a(iayu) IPAddressDeny = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPIngressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as IPEgressFilterPath = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly as DisableControllers = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i KillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i RestartKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i FinalKillSignal = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGKILL = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b SendSIGHUP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly i WatchdogSignal = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; + interface org.freedesktop.systemd1.Unit { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Abandon() may be used to place a scope unit in the "abandoned" state. This + may be used to inform the system manager that the manager that created the scope lost interest in the + scope (for example, because it is terminating), without wanting to shut down the scope entirely. + + + + Signals + + RequestStop is sent to the peer that is configured in the + Controller property when systemd is requested to terminate the scope unit. A program + registering a scope can use this to cleanly shut down the processes it added to the scope instead of + letting systemd do it with the usual SIGTERM logic. + + + + Properties + + All properties correspond directly with the matching properties of service units. + + Controller contains the bus name (unique or well-known) that is notified when + the scope unit is to be shut down via a RequestStop signal (see below). This is + set when the scope is created. If not set, the scope's processes will terminated with + SIGTERM directly. + + + + + + Job Objects + + Job objects encapsulate scheduled or running jobs. Each unit can have none or one jobs in the + execution queue. Each job is attached to exactly one unit. + + +node /org/freedesktop/systemd1/job/666 { + interface org.freedesktop.systemd1.Job { + methods: + Cancel(); + GetAfter(out a(usssoo) jobs); + GetBefore(out a(usssoo) jobs); + properties: + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u Id = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly (so) Unit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s JobType = '...'; + readonly s State = '...'; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Cancel() cancels the job. Note that this will remove a job from the queue if + it is not yet executed but generally will not cause a job that is already in the process of being + executed to be aborted. This operation may also be requested via the CancelJob() + method of the Manager object (see above), which is sometimes useful to reduce roundtrips. + + + + Properties + + Id is the numeric Id of the job. During the runtime of a systemd instance each + numeric ID is only assigned once. + + Unit refers to the unit this job belongs to. It is a structure consisting of + the name of the unit and a bus path to the unit's object. + + JobType refers to the job's type and is one of start, + verify-active, stop, reload, + restart, try-restart, or reload-or-start. Note + that later versions might define additional values. + + State refers to the job's state and is one of waiting and + running. The former indicates that a job is currently queued but has not begun to + execute yet. The latter indicates that a job is currently being executed. + + + + + Examples + + + Introspect <interfacename>org.freedesktop.systemd1.Manager</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.systemd1 \ + --object-path /org/freedesktop/systemd1 + + + + + Introspect a unit on the bus + + +$ busctl introspect org.freedesktop.systemd1 \ + $(busctl call org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + GetUnit s systemd-resolved.service | cut -d'"' -f2) + + + + + Introspect <interfacename>org.freedesktop.systemd1.Job</interfacename> on the bus + + +$ gdbus introspect --system --dest org.freedesktop.systemd1 \ + --object-path /org/freedesktop/systemd1/job/1292 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + diff --git a/man/org.freedesktop.timedate1.xml b/man/org.freedesktop.timedate1.xml new file mode 100644 index 000000000..325c3acfb --- /dev/null +++ b/man/org.freedesktop.timedate1.xml @@ -0,0 +1,205 @@ + + + + + + + org.freedesktop.timedate1 + systemd + + + + org.freedesktop.timedate1 + 5 + + + + org.freedesktop.timedate1 + The D-Bus interface of systemd-timedated + + + + Introduction + + + systemd-timedated.service8 + is a system service that can be used to control the system time and related settings. This page + describes the D-Bus interface. + + + + The D-Bus API + + The service exposes the following interfaces on the bus: + + +node /org/freedesktop/timedate1 { + interface org.freedesktop.timedate1 { + methods: + SetTime(in x usec_utc, + in b relative, + in b interactive); + SetTimezone(in s timezone, + in b interactive); + SetLocalRTC(in b local_rtc, + in b fix_system, + in b interactive); + SetNTP(in b use_ntp, + in b interactive); + ListTimezones(out as timezones); + properties: + readonly s Timezone = '...'; + readonly b LocalRTC = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b CanNTP = ...; + readonly b NTP = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly b NTPSynchronized = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t TimeUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t RTCTimeUSec = ...; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; + interface org.freedesktop.DBus.Properties { ... }; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Methods + + Use SetTime() to change the system clock. Pass a value of microseconds since + the UNIX epoch (1 Jan 1970 UTC). If relative is true, the passed usec value will be + added to the current system time. If it is false, the current system time will be set to the passed usec + value. If the system time is set with this method, the RTC will be updated as well. + + Use SetTimezone() to set the system timezone. Pass a value like + Europe/Berlin to set the timezone. Valid timezones are listed in + /usr/share/zoneinfo/zone.tab. If the RTC is configured to be maintained in local + time, it will be updated accordingly. + + Use SetLocalRTC() to control whether the RTC is in local time or UTC. It is + strongly recommended to maintain the RTC in UTC. However, some OSes (Windows) maintain the RTC in local + time, which might make it necessary to enable this feature. Note that this might create various problems as + daylight changes could be missed. If fix_system is true, + the time from the RTC is read again and the system clock is adjusted according to the new setting. If + fix_system is false, the system time is written to the RTC + taking the new setting into account. Use fix_system=true in installers and livecds + where the RTC is probably more reliable than the system time. Use fix_system=false + in configuration UIs that are run during normal operation and where the system clock is probably more + reliable than the RTC. + + Use SetNTP() to control whether the system clock is synchronized with the + network using systemd-timesyncd. This will enable and start or disable and stop + the chosen time synchronization service. + + ListTimezones() returns a list of time zones known on the local system as an + array of names (["Africa/Abidjan", "Africa/Accra", ..., "UTC"]). + + + + Properties + + Timezone shows the currently configured time zone. + LocalRTC shows whether the RTC is configured to use UTC (false), or the local time + zone (true). CanNTP shows whether a service to perform time synchronization over the + network is available, and NTP shows whether such a service is enabled. + + NTPSynchronized shows whether the kernel reports the time as synchronized + (c.f. + adjtimex3). + TimeUSec and RTCTimeUSec show the current time on the system and + in the RTC. The purpose of those three properties is to allow remote clients to access this information + over D-Bus. Local clients can access the information directly. + + Whenever the Timezone and LocalRTC settings are changed via + the daemon, PropertyChanged signals are sent out to which clients can subscribe. + + + Note that this service will not inform you about system time changes. Use + timerfd3 + with CLOCK_REALTIME and TFD_TIMER_CANCEL_ON_SET for that. + + + + + Security + + The interactive boolean parameters can be used to control whether + polkit + should interactively ask the user for authentication credentials if required. + + The polkit action for SetTimezone() is + org.freedesktop.timedate1.set-timezone. For + SetLocalRTC() it is + org.freedesktop.timedate1.set-local-rtc, for + SetTime() it is org.freedesktop.timedate1.set-time + and for SetNTP() it is + org.freedesktop.timedate1.set-ntp. + ListTimezones() does not require any privileges. + + + + + + Examples + + + Introspect <interfacename>org.freedesktop.timedate1</interfacename> on the bus + + +$ gdbus introspect --system \ + --dest org.freedesktop.timedate1 \ + --object-path /org/freedesktop/timedate1 + + + + + + Versioning + + These D-Bus interfaces follow + the usual interface versioning guidelines. + + + + See also + + More information on how the system clock and RTC interact + + diff --git a/man/os-release.xml b/man/os-release.xml index 5a5e318f7..a2164436c 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -192,13 +192,11 @@ ANSI_COLOR= - A suggested presentation color when showing - the OS name on the console. This should be specified as string - suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code - for setting graphical rendition. This field is optional. - Example: ANSI_COLOR="0;31" for red, or - ANSI_COLOR="1;34" for light - blue. + A suggested presentation color when showing the OS name on the console. This should + be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting + graphical rendition. This field is optional. Example: ANSI_COLOR="0;31" for red, + ANSI_COLOR="1;34" for light blue, or + ANSI_COLOR="0;38;2;60;110;180" for Fedora blue. @@ -220,7 +218,7 @@ BUG_REPORT_URL= PRIVACY_POLICY_URL= - Links to resources on the Internet related to + Links to resources on the Internet related to the operating system. HOME_URL= should refer to the homepage of the operating system, or alternatively some homepage of the @@ -341,20 +339,36 @@ name in order to avoid name clashes. Applications reading this file must ignore unknown fields. Example: DEBIAN_BTS="debbugs://bugs.debian.org/" + + Container and sandbox runtime managers may make the host's + identification data available to applications by providing the host's + /etc/os-release (if available, otherwise + /usr/lib/os-release as a fallback) as + /run/host/os-release. Example NAME=Fedora -VERSION="17 (Beefy Miracle)" +VERSION="32 (Workstation Edition)" ID=fedora -VERSION_ID=17 -PRETTY_NAME="Fedora 17 (Beefy Miracle)" -ANSI_COLOR="0;34" -CPE_NAME="cpe:/o:fedoraproject:fedora:17" +VERSION_ID=32 +PRETTY_NAME="Fedora 32 (Workstation Edition)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:32" HOME_URL="https://fedoraproject.org/" -BUG_REPORT_URL="https://bugzilla.redhat.com/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f32/system-administrators-guide/" +SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=32 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=32 +PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" +VARIANT="Workstation Edition" +VARIANT_ID=workstation diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 70927d737..609743be6 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -153,7 +153,7 @@ hence be used to uniquely label files or other resources of this session. Combine this ID with the boot identifier, as returned by sd_id128_get_boot3, for a - globally unique identifier for the current session. + globally unique identifier. @@ -253,7 +253,7 @@ See systemd.resource-control5 for more information about the resources. - Also, see pam_set_data3 for additional information about how to set + Also, see pam_set_data3 for additional information about how to set the context objects. diff --git a/man/pam_systemd_home.xml b/man/pam_systemd_home.xml index 341f8a8e4..ab02f9833 100644 --- a/man/pam_systemd_home.xml +++ b/man/pam_systemd_home.xml @@ -51,8 +51,34 @@ coming back from suspend. It is recommended to set this parameter for all PAM applications that have support for automatically re-authenticating via PAM on system resume. If multiple sessions of the same user are open in parallel the user's home directory will be left unsuspended on system suspend - as long as at least one of the sessions does not set this parameter. Defaults to - off. + as long as at least one of the sessions does not set this parameter to on. Defaults to + off. + + Note that TTY logins generally do not support re-authentication on system resume. + Re-authentication on system resume is primarily a concept implementable in graphical environments, in + the form of lock screens brought up automatically when the system goes to sleep. This means that if a + user concurrently uses graphical login sessions that implement the required re-authentication + mechanism and console logins that do not, the home directory is not locked during suspend, due to the + logic explained above. That said, it is possible to set this field for TTY logins too, ignoring the + fact that TTY logins actually don't support the re-authentication mechanism. In that case the TTY + sessions will appear hung until the user logs in on another virtual terminal (regardless if via + another TTY session or graphically) which will resume the home directory and unblock the original TTY + session. (Do note that lack of screen locking on TTY sessions means even though the TTY session + appears hung, keypresses can still be queued into it, and the existing screen contents be read + without re-authentication; this limitation is unrelated to the home directory management + pam_systemd_home and systemd-homed.service implement.) + + Turning this option on by default is highly recommended for all sessions, but only if the + service managing these sessions correctly implements the aforementioned re-authentication. Note that + the re-authentication must take place from a component running outside of the user's context, so that + it does not require access to the user's home directory for operation. Traditionally, most desktop + environments do not implement screen locking this way, and need to be updated + accordingly. + + This setting may also be controlled via the $SYSTEMD_HOME_SUSPEND + environment variable (see below), which pam_systemd_home reads during initialization and sets + for sessions. If both the environment variable is set and the module parameter specified the latter + takes precedence. @@ -84,6 +110,15 @@ Indicates that the user's home directory is managed by systemd-homed.service. + + $SYSTEMD_HOME_SUSPEND= + + Indicates whether the session has been registered with the suspend mechanism enabled + or disabled (see above). The variable's value is either 0 or + 1. Note that the module both reads the variable when initializing, and sets it for + sessions. + + diff --git a/man/path-documents.c b/man/path-documents.c new file mode 100644 index 000000000..a6c1f9371 --- /dev/null +++ b/man/path-documents.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) { + char *t; + + sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t); + printf("~/Documents: %s\n", t); +} diff --git a/man/portablectl.xml b/man/portablectl.xml index f2d8da40c..962429683 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -118,7 +118,7 @@ By default all unit files whose names start with a prefix generated from the image's file name are copied out. Specifically, the prefix is determined from the image file name with any suffix such as - .raw removed, truncated at the first occurrence of and underscore character + .raw removed, truncated at the first occurrence of an underscore character (_), if there is one. The underscore logic is supposed to be used to versioning so that the an image file foobar_47.11.raw will result in a unit file matching prefix of foobar. This prefix is then compared with all unit files names contained in the image in @@ -403,7 +403,7 @@
- For details on this profiles, and their effects please have a look at their precise definitions, + For details on these profiles and their effects see their precise definitions, e.g. /usr/lib/systemd/portable/profile/default/service.conf and similar.
diff --git a/man/pstore.conf.xml b/man/pstore.conf.xml index 2b9c8b1a7..501171e78 100644 --- a/man/pstore.conf.xml +++ b/man/pstore.conf.xml @@ -44,7 +44,7 @@ Options All options are configured in the - [PStore] section: + [PStore] section:
@@ -82,7 +82,7 @@ See Also - systemd-journald.service8, + systemd-journald.service8 diff --git a/man/repart.d.xml b/man/repart.d.xml index ae8b17ff9..1b104e76d 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -1,7 +1,8 @@ - + repart.d @@ -139,7 +140,7 @@ root-secondary - Root file system partition of the secondary architecture of the local architecture; usually the matching 32bit architecture for the local 64bit architecture) + Root file system partition of the secondary architecture of the local architecture (usually the matching 32bit architecture for the local 64bit architecture) @@ -214,7 +215,17 @@ setting is not used for matching. It is also not used when a label is already set for an existing partition. It is thus only used when a partition is newly created or when an existing one had a no label set (that is: an empty label). If not specified a label derived from the partition type is - automatically used.
+ automatically used. Simple specifier expansion is supported, see below. +
+ + + UUID= + + The UUID to assign to the partition if none is assigned yet. Note that this + setting is not used for matching. It is also not used when a UUID is already set for an existing + partition. It is thus only used when a partition is newly created or when an existing one had a + all-zero UUID set. If not specified a UUID derived from the partition type is automatically + used. @@ -289,13 +300,36 @@ PaddingMinBytes= PaddingMaxBytes= - Specifies minimum and maximum size constrains in bytes for the free space after the + Specifies minimum and maximum size constraints in bytes for the free space after the partition (the "padding"). Semantics are similar to SizeMinBytes= and SizeMaxBytes=, except that unlike partition sizes free space can be shrunk and can be as small as zero. By default no size constraints on padding are set, so that only PaddingWeight= determines the size of the padding applied. + + CopyBlocks= + + Takes a path to a regular file, block device node or directory. If specified and the + partition is newly created the data from the specified path is written to the newly created + partition, on the block level. If a directory is specified the backing block device of the file + system the directory is on is determined and the data read directly from that. This option is useful + to efficiently replicate existing file systems on the block level on a new partition, for example to + build a simple OS installer or OS image builder. + + The file specified here must have a size that is a multiple of the basic block size 512 and not + be empty. If this option is used, the size allocation algorithm is slightly altered: the partition is + created as least as big as required to fit the data in, i.e. the data size is an additional minimum + size value taken into consideration for the allocation algorithm, similar to and in addition to the + SizeMin= value configured above. + + This option has no effect if the partition it is declared for already exists, i.e. existing + data is never overwritten. Note that the data is copied in before the partition table is updated, + i.e. before the partition actually is persistently created. This provides robustness: it is + guaranteed that the partition either doesn't exist or exists fully populated; it is not possible that + the partition exists but is not or only partially populated. + + FactoryReset= @@ -306,6 +340,40 @@ + + Specifiers + + Specifiers may be used in the Label= setting. The following expansions are understood: + + Specifiers available + + + + + + + Specifier + Meaning + Details + + + + + + + + + + + + + + + + +
+
+ Examples diff --git a/man/resolvectl.xml b/man/resolvectl.xml index a7de5a309..a4bd8f52d 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -45,7 +45,7 @@ interface the data was discovered. It also contains information on whether the information could be authenticated. All data for which local DNSSEC validation succeeds is considered authenticated. Moreover all data originating from local, trusted sources is also reported authenticated, including resolution of the local host - name, the localhost host name or all data from /etc/hosts. + name, the localhost hostname or all data from /etc/hosts. @@ -145,15 +145,20 @@ settings for network interfaces. These commands may be used to inform systemd-resolved or systemd-networkd about per-interface DNS configuration determined through external means. The dns command expects IPv4 or - IPv6 address specifications of DNS servers to use. The domain command expects - valid DNS domains, possibly prefixed with ~, and configures a per-interface - search or route-only domain. The default-route command expects a boolean - parameter, and configures whether the link may be used as default route for DNS lookups, i.e. if it - is suitable for lookups on domains no other link explicitly is configured for. The - llmnr, mdns, dnssec and - dnsovertls commands may be used to configure the per-interface LLMNR, - MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, nta command may be used - to configure additional per-interface DNSSEC NTA domains. + IPv6 address specifications of DNS servers to use. Each address can optionally take a port number + separated with :, a network interface name or index separated with + %, and a Server Name Indication (SNI) separated with #. When + IPv6 address is specified with a port number, then the address must be in the square brackets. That + is, the acceptable full formats are 111.222.333.444:9953%ifname#example.com for + IPv4 and [1111:2222::3333]:9953%ifname#example.com for IPv6. The + domain command expects valid DNS domains, possibly prefixed with + ~, and configures a per-interface search or route-only domain. The + default-route command expects a boolean parameter, and configures whether the + link may be used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no + other link explicitly is configured for. The llmnr, mdns, + dnssec and dnsovertls commands may be used to configure the + per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, nta + command may be used to configure additional per-interface DNSSEC NTA domains. Commands dns, domain and nta can take a single empty string argument to clear their respective value lists. @@ -175,6 +180,7 @@ automatically, an explicit reverting is not necessary in that case.
+ @@ -315,11 +321,11 @@ Registers per-interface DNS configuration data with systemd-resolved. Expects a network interface name as only command line argument. Reads - resolv.conf5 compatible DNS - configuration data from its standard input. Relevant fields are nameserver and + resolv.conf5-compatible + DNS configuration data from its standard input. Relevant fields are nameserver and domain/search. This command is mostly identical to invoking - resolvectl with a combination of and - commands. + resolvectl with a combination of and + commands. diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 0f70ced5b..535a23f50 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -41,14 +41,19 @@ Options - The following options are available in the [Resolve] section: + The following options are available in the [Resolve] section: DNS= - A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests - are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from + A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. Each address can + optionally take a port number separated with :, a network interface name or index separated with + %, and a Server Name Indication (SNI) separated with #. When IPv6 address is + specified with a port number, then the address must be in the square brackets. That is, the acceptable full formats + are 111.222.333.444:9953%ifname#example.com for IPv4 and + [1111:2222::3333]:9953%ifname#example.com for IPv6. DNS requests are sent to one of the listed + DNS servers in parallel to suitable per-link DNS servers acquired from systemd-networkd.service8 or set at runtime by external applications. For compatibility reasons, if this setting is not specified, the DNS servers listed in /etc/resolv.conf are used instead, if that file exists and any servers @@ -57,8 +62,8 @@ FallbackDNS= - A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any - per-link DNS servers obtained from + A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Please see + DNS= for acceptable format of adddresses. Any per-link DNS servers obtained from systemd-networkd.service8 take precedence over this setting, as do any servers set via DNS= above or /etc/resolv.conf. This setting is hence only used if no other DNS server information is @@ -67,20 +72,28 @@ Domains= - A space-separated list of domains. These domains are used as search suffixes when resolving - single-label host names (domain names which contain no dot), in order to qualify them into fully-qualified - domain names (FQDNs). Search domains are strictly processed in the order they are specified, until the name - with the suffix appended is found. For compatibility reasons, if this setting is not specified, the search - domains listed in /etc/resolv.conf are used instead, if that file exists and any domains - are configured in it. This setting defaults to the empty list. + A space-separated list of domains optionally prefixed with ~, + used for two distinct purposes described below. Defaults to the empty list. - Specified domain names may optionally be prefixed with ~. In this case they do not - define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured - with the system DNS= setting (see above), in case additional, suitable per-link DNS servers - are known. If no per-link DNS servers are known using the ~ syntax has no effect. Use the - construct ~. (which is composed of ~ to indicate a routing domain and - . to indicate the DNS root domain that is the implied suffix of all DNS domains) to use the - system DNS server defined with DNS= preferably for all domains. + Any domains not prefixed with ~ are used as search + suffixes when resolving single-label hostnames (domain names which contain no dot), in order to + qualify them into fully-qualified domain names (FQDNs). These "search domains" are strictly processed + in the order they are specified in, until the name with the suffix appended is found. For + compatibility reasons, if this setting is not specified, the search domains listed in + /etc/resolv.conf with the search keyword are used instead, if + that file exists and any domains are configured in it. + + The domains prefixed with ~ are called "routing domains". All domains listed + here (both search domains and routing domains after removing the ~ prefix) define + a search path that preferably directs DNS queries to this interface. This search path has an effect + only when suitable per-link DNS servers are known. Such servers may be defined through the + DNS= setting (see above) and dynamically at run time, for example from DHCP + leases. If no per-link DNS servers are known, routing domains have no effect. + + Use the construct ~. (which is composed from ~ to + indicate a routing domain and . to indicate the DNS root domain that is the + implied suffix of all DNS domains) to use the DNS servers defined for this link preferably for all + domains. @@ -193,11 +206,17 @@ DNSOverTLS= - Takes a boolean argument or opportunistic. - If true all connections to the server will be encrypted. Note that - this mode requires a DNS server that supports DNS-over-TLS and has - a valid certificate for it's IP. If the DNS server does not support - DNS-over-TLS all DNS requests will fail. When set to opportunistic + Takes a boolean argument or opportunistic. If + true all connections to the server will be encrypted. Note that this + mode requires a DNS server that supports DNS-over-TLS and has a valid + certificate. If the hostname was specified in DNS= + by using the format format address#server_name it + is used to validate its certificate and also to enable Server Name + Indication (SNI) when opening a TLS connection. Otherwise + the certificate is checked against the server's IP. + If the DNS server does not support DNS-over-TLS all DNS requests will fail. + + When set to opportunistic DNS request are attempted to send encrypted with DNS-over-TLS. If the DNS server does not support TLS, DNS-over-TLS is disabled. Note that this mode makes DNS-over-TLS vulnerable to "downgrade" @@ -214,9 +233,6 @@ resolver is not capable of authenticating the server, so it is vulnerable to "man-in-the-middle" attacks. - Server Name Indication (SNI) can be used when opening a TLS connection. - Entries in DNS= should be in format address#server_name. - In addition to this global DNSOverTLS setting systemd-networkd.service8 also maintains per-link DNSOverTLS settings. For system DNS @@ -231,11 +247,11 @@ Cache= - Takes a boolean or no-negative as argument. If yes (the default), resolving a domain name - which already got queried earlier will return the previous result as long as it is still valid, and thus does - not result in a new network request. Be aware that turning off caching comes at a performance penalty, which - is particularly high when DNSSEC is used. - If no-negative, only positive answers are cached. + Takes a boolean or no-negative as argument. If + yes (the default), resolving a domain name which already got queried earlier will + return the previous result as long as it is still valid, and thus does not result in a new network + request. Be aware that turning off caching comes at a performance penalty, which is particularly high + when DNSSEC is used. If no-negative, only positive answers are cached. Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching. @@ -255,11 +271,28 @@ ReadEtcHosts= - Takes a boolean argument. If yes (the default), the DNS stub resolver will read - /etc/hosts, and try to resolve hosts or address by using the entries in the file before - sending query to DNS servers. + Takes a boolean argument. If yes (the default), + systemd-resolved will read /etc/hosts, and try to resolve + hosts or address by using the entries in the file before sending query to DNS servers. + + + ResolveUnicastSingleLabel= + Takes a boolean argument. When false (the default), + systemd-resolved will not resolve A and AAAA queries for single-label names over + classic DNS. Note that such names may still be resolved if search domains are specified (see + Domains= above), or using other mechanisms, in particular via LLMNR or from + /etc/hosts. When true, queries for single-label names will be forwarded to + global DNS servers even if no search domains are defined. + + + This option is provided for compatibility with configurations where public DNS + servers are not used. Forwarding single-label names to servers not under your control is + not standard-conformant, see IAB + Statement, and may create a privacy and security risk. + diff --git a/man/rules/meson.build b/man/rules/meson.build index 296dd7da3..3fb454faa 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -1,4 +1,4 @@ -# Do not edit. Generated by make-man-rules.py. +# Do not edit. Generated by update-man-rules.py. # Update with: # ninja -C build man/update-man-rules manpages = [ @@ -18,6 +18,7 @@ manpages = [ ['file-hierarchy', '7', [], ''], ['halt', '8', ['poweroff', 'reboot'], ''], ['homectl', '1', [], 'ENABLE_HOMED'], + ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'], ['hostname', '5', [], ''], ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'], ['hwdb', '7', [], 'ENABLE_HWDB'], @@ -44,6 +45,16 @@ manpages = [ ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_NSS_MYMACHINES'], ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_NSS_RESOLVE'], ['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'], + ['org.freedesktop.LogControl1', '5', [], ''], + ['org.freedesktop.home1', '5', [], 'ENABLE_HOMED'], + ['org.freedesktop.hostname1', '5', [], 'ENABLE_HOSTNAMED'], + ['org.freedesktop.import1', '5', [], 'ENABLE_IMPORTD'], + ['org.freedesktop.locale1', '5', [], 'ENABLE_LOCALED'], + ['org.freedesktop.login1', '5', [], 'ENABLE_LOGIND'], + ['org.freedesktop.machine1', '5', [], 'ENABLE_MACHINED'], + ['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'], + ['org.freedesktop.systemd1', '5', [], ''], + ['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'], ['os-release', '5', [], ''], ['pam_systemd', '8', [], 'HAVE_PAM'], ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'], @@ -99,6 +110,7 @@ manpages = [ 'SD_WARNING'], ''], ['sd-event', '3', [], ''], + ['sd-hwdb', '3', [], ''], ['sd-id128', '3', ['SD_ID128_CONST_STR', @@ -121,7 +133,8 @@ manpages = [ 'sd_bus_match_signal', 'sd_bus_match_signal_async'], ''], - ['sd_bus_add_object_vtable', + ['sd_bus_add_node_enumerator', '3', [], ''], + ['sd_bus_add_object', '3', ['SD_BUS_METHOD', 'SD_BUS_METHOD_WITH_NAMES', @@ -134,10 +147,22 @@ manpages = [ 'SD_BUS_VTABLE_END', 'SD_BUS_VTABLE_START', 'SD_BUS_WRITABLE_PROPERTY', - 'sd_bus_add_fallback_vtable'], + 'sd_bus_add_fallback', + 'sd_bus_add_fallback_vtable', + 'sd_bus_add_filter', + 'sd_bus_add_object_vtable'], ''], + ['sd_bus_add_object_manager', '3', [], ''], ['sd_bus_attach_event', '3', ['sd_bus_detach_event', 'sd_bus_get_event'], ''], - ['sd_bus_close', '3', ['sd_bus_flush'], ''], + ['sd_bus_call', '3', ['sd_bus_call_async'], ''], + ['sd_bus_call_method', + '3', + ['sd_bus_call_method_async', + 'sd_bus_call_method_asyncv', + 'sd_bus_call_methodv'], + ''], + ['sd_bus_can_send', '3', [], ''], + ['sd_bus_close', '3', ['sd_bus_default_flush_close', 'sd_bus_flush'], ''], ['sd_bus_creds_get_pid', '3', ['sd_bus_creds_get_audit_login_uid', @@ -195,6 +220,18 @@ manpages = [ 'sd_bus_open_user_with_description', 'sd_bus_open_with_description'], ''], + ['sd_bus_emit_signal', + '3', + ['sd_bus_emit_interfaces_added', + 'sd_bus_emit_interfaces_added_strv', + 'sd_bus_emit_interfaces_removed', + 'sd_bus_emit_interfaces_removed_strv', + 'sd_bus_emit_object_added', + 'sd_bus_emit_object_removed', + 'sd_bus_emit_properties_changed', + 'sd_bus_emit_properties_changed_strv', + 'sd_bus_emit_signalv'], + ''], ['sd_bus_enqueue_for_read', '3', [], ''], ['sd_bus_error', '3', @@ -217,9 +254,27 @@ manpages = [ '3', ['SD_BUS_ERROR_END', 'SD_BUS_ERROR_MAP', 'sd_bus_error_map'], ''], - ['sd_bus_get_fd', '3', ['sd_bus_get_events', 'sd_bus_get_timeout'], ''], + ['sd_bus_get_current_handler', + '3', + ['sd_bus_get_current_message', + 'sd_bus_get_current_slot', + 'sd_bus_get_current_userdata'], + ''], + ['sd_bus_get_fd', + '3', + ['sd_bus_get_events', 'sd_bus_get_timeout', 'sd_bus_set_fd'], + ''], ['sd_bus_get_n_queued_read', '3', ['sd_bus_get_n_queued_write'], ''], + ['sd_bus_get_name_creds', '3', ['sd_bus_get_owner_creds'], ''], + ['sd_bus_get_name_machine_id', '3', [], ''], + ['sd_bus_interface_name_is_valid', + '3', + ['sd_bus_member_name_is_valid', + 'sd_bus_object_path_is_valid', + 'sd_bus_service_name_is_valid'], + ''], ['sd_bus_is_open', '3', ['sd_bus_is_ready'], ''], + ['sd_bus_list_names', '3', [], ''], ['sd_bus_message_append', '3', ['sd_bus_message_appendv'], ''], ['sd_bus_message_append_array', '3', @@ -233,6 +288,7 @@ manpages = [ ['sd_bus_message_append_string_iovec', 'sd_bus_message_append_string_space'], ''], ['sd_bus_message_append_strv', '3', [], ''], + ['sd_bus_message_at_end', '3', [], ''], ['sd_bus_message_copy', '3', [], ''], ['sd_bus_message_dump', '3', [], ''], ['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''], @@ -246,7 +302,10 @@ manpages = [ ''], ['sd_bus_message_get_type', '3', - ['sd_bus_message_is_method_call', + ['sd_bus_message_get_creds', + 'sd_bus_message_get_errno', + 'sd_bus_message_get_error', + 'sd_bus_message_is_method_call', 'sd_bus_message_is_method_error', 'sd_bus_message_is_signal'], ''], @@ -272,10 +331,21 @@ manpages = [ 'sd_bus_message_new_method_errorf'], ''], ['sd_bus_message_new_signal', '3', [], ''], - ['sd_bus_message_read', '3', ['sd_bus_message_readv'], ''], + ['sd_bus_message_open_container', + '3', + ['sd_bus_message_close_container', + 'sd_bus_message_enter_container', + 'sd_bus_message_exit_container'], + ''], + ['sd_bus_message_read', + '3', + ['sd_bus_message_peek_type', 'sd_bus_message_readv'], + ''], ['sd_bus_message_read_array', '3', [], ''], ['sd_bus_message_read_basic', '3', [], ''], + ['sd_bus_message_read_strv', '3', [], ''], ['sd_bus_message_rewind', '3', [], ''], + ['sd_bus_message_seal', '3', [], ''], ['sd_bus_message_sensitive', '3', [], ''], ['sd_bus_message_set_destination', '3', @@ -288,15 +358,19 @@ manpages = [ ''], ['sd_bus_message_set_expect_reply', '3', - ['sd_bus_message_get_auto_start', + ['sd_bus_message_get_allow_interactive_authorization', + 'sd_bus_message_get_auto_start', 'sd_bus_message_get_expect_reply', + 'sd_bus_message_set_allow_interactive_authorization', 'sd_bus_message_set_auto_start'], ''], ['sd_bus_message_skip', '3', [], ''], ['sd_bus_message_verify_type', '3', [], ''], ['sd_bus_negotiate_fds', '3', - ['sd_bus_negotiate_creds', 'sd_bus_negotiate_timestamp'], + ['sd_bus_get_creds_mask', + 'sd_bus_negotiate_creds', + 'sd_bus_negotiate_timestamp'], ''], ['sd_bus_new', '3', @@ -313,34 +387,70 @@ manpages = [ ['sd_bus_path_decode', 'sd_bus_path_decode_many', 'sd_bus_path_encode_many'], ''], ['sd_bus_process', '3', [], ''], + ['sd_bus_query_sender_creds', '3', ['sd_bus_query_sender_privilege'], ''], ['sd_bus_reply_method_error', '3', ['sd_bus_reply_method_errno', 'sd_bus_reply_method_errnof', - 'sd_bus_reply_method_errorf'], + 'sd_bus_reply_method_errnofv', + 'sd_bus_reply_method_errorf', + 'sd_bus_reply_method_errorfv'], ''], + ['sd_bus_reply_method_return', '3', ['sd_bus_reply_method_returnv'], ''], ['sd_bus_request_name', '3', ['sd_bus_release_name', 'sd_bus_release_name_async', 'sd_bus_request_name_async'], ''], + ['sd_bus_send', '3', ['sd_bus_send_to'], ''], + ['sd_bus_set_address', '3', ['sd_bus_get_address', 'sd_bus_set_exec'], ''], ['sd_bus_set_close_on_exit', '3', ['sd_bus_get_close_on_exit'], ''], ['sd_bus_set_connected_signal', '3', ['sd_bus_get_connected_signal'], ''], ['sd_bus_set_description', '3', ['sd_bus_get_allow_interactive_authorization', 'sd_bus_get_description', + 'sd_bus_get_scope', + 'sd_bus_get_tid', + 'sd_bus_get_unique_name', + 'sd_bus_is_anonymous', + 'sd_bus_is_trusted', 'sd_bus_set_allow_interactive_authorization', 'sd_bus_set_anonymous', 'sd_bus_set_trusted'], ''], - ['sd_bus_set_sender', '3', ['sd_bus_get_sender'], ''], - ['sd_bus_set_watch_bind', '3', ['sd_bus_get_watch_bind'], ''], - ['sd_bus_slot_ref', + ['sd_bus_set_exit_on_disconnect', '3', ['sd_bus_get_exit_on_disconnect'], ''], + ['sd_bus_set_method_call_timeout', '3', - ['sd_bus_slot_get_bus', 'sd_bus_slot_unref', 'sd_bus_slot_unrefp'], + ['sd_bus_get_method_call_timeout'], ''], + ['sd_bus_set_property', + '3', + ['sd_bus_get_property', + 'sd_bus_get_property_string', + 'sd_bus_get_property_strv', + 'sd_bus_get_property_trivial', + 'sd_bus_set_propertyv'], + ''], + ['sd_bus_set_sender', '3', ['sd_bus_get_sender'], ''], + ['sd_bus_set_server', + '3', + ['sd_bus_get_bus_id', + 'sd_bus_is_bus_client', + 'sd_bus_is_monitor', + 'sd_bus_is_server', + 'sd_bus_set_bus_client', + 'sd_bus_set_monitor'], + ''], + ['sd_bus_set_watch_bind', '3', ['sd_bus_get_watch_bind'], ''], + ['sd_bus_slot_get_bus', + '3', + ['sd_bus_slot_get_current_handler', + 'sd_bus_slot_get_current_message', + 'sd_bus_slot_get_current_userdata'], + ''], + ['sd_bus_slot_ref', '3', ['sd_bus_slot_unref', 'sd_bus_slot_unrefp'], ''], ['sd_bus_slot_set_description', '3', ['sd_bus_slot_get_description'], ''], ['sd_bus_slot_set_destroy_callback', '3', @@ -351,6 +461,7 @@ manpages = [ ''], ['sd_bus_slot_set_floating', '3', ['sd_bus_slot_get_floating'], ''], ['sd_bus_slot_set_userdata', '3', ['sd_bus_slot_get_userdata'], ''], + ['sd_bus_start', '3', [], ''], ['sd_bus_track_add_name', '3', ['sd_bus_track_add_sender', @@ -486,6 +597,11 @@ manpages = [ '3', ['sd_get_machine_names', 'sd_get_sessions', 'sd_get_uids'], 'HAVE_PAM'], + ['sd_hwdb_get', + '3', + ['SD_HWDB_FOREACH_PROPERTY', 'sd_hwdb_enumerate', 'sd_hwdb_seek'], + ''], + ['sd_hwdb_new', '3', ['sd_hwdb_ref', 'sd_hwdb_unref'], ''], ['sd_id128_get_machine', '3', ['sd_id128_get_boot', @@ -523,6 +639,7 @@ manpages = [ ['sd_journal_get_data', '3', ['SD_JOURNAL_FOREACH_DATA', + 'sd_journal_enumerate_available_data', 'sd_journal_enumerate_data', 'sd_journal_get_data_threshold', 'sd_journal_restart_data', @@ -564,19 +681,26 @@ manpages = [ 'sd_journal_open_directory', 'sd_journal_open_directory_fd', 'sd_journal_open_files', - 'sd_journal_open_files_fd'], + 'sd_journal_open_files_fd', + 'sd_journal_open_namespace'], ''], ['sd_journal_print', '3', ['SD_JOURNAL_SUPPRESS_LOCATION', 'sd_journal_perror', + 'sd_journal_perror_with_location', + 'sd_journal_print_with_location', 'sd_journal_printv', + 'sd_journal_printv_with_location', 'sd_journal_send', - 'sd_journal_sendv'], + 'sd_journal_send_with_location', + 'sd_journal_sendv', + 'sd_journal_sendv_with_location'], ''], ['sd_journal_query_unique', '3', ['SD_JOURNAL_FOREACH_UNIQUE', + 'sd_journal_enumerate_available_unique', 'sd_journal_enumerate_unique', 'sd_journal_restart_unique'], ''], @@ -605,8 +729,13 @@ manpages = [ ['sd_machine_get_class', '3', ['sd_machine_get_ifindices'], ''], ['sd_notify', '3', - ['sd_notifyf', 'sd_pid_notify', 'sd_pid_notify_with_fds', 'sd_pid_notifyf'], + ['sd_notify_barrier', + 'sd_notifyf', + 'sd_pid_notify', + 'sd_pid_notify_with_fds', + 'sd_pid_notifyf'], ''], + ['sd_path_lookup', '3', ['sd_path_lookup_strv'], ''], ['sd_pid_get_owner_uid', '3', ['sd_peer_get_cgroup', @@ -627,10 +756,7 @@ manpages = [ 'HAVE_PAM'], ['sd_seat_get_active', '3', - ['sd_seat_can_graphical', - 'sd_seat_can_multi_session', - 'sd_seat_can_tty', - 'sd_seat_get_sessions'], + ['sd_seat_can_graphical', 'sd_seat_can_tty', 'sd_seat_get_sessions'], 'HAVE_PAM'], ['sd_session_is_active', '3', @@ -726,7 +852,7 @@ manpages = [ ['systemd-initctl.service', '8', ['systemd-initctl', 'systemd-initctl.socket'], - ''], + 'HAVE_SYSV_COMPAT'], ['systemd-journal-gatewayd.service', '8', ['systemd-journal-gatewayd', 'systemd-journal-gatewayd.socket'], @@ -851,6 +977,7 @@ manpages = [ ['systemd-veritysetup'], 'HAVE_LIBCRYPTSETUP'], ['systemd-volatile-root.service', '8', ['systemd-volatile-root'], ''], + ['systemd-xdg-autostart-generator', '8', [], 'ENABLE_XDG_AUTOSTART'], ['systemd', '1', ['init'], ''], ['systemd.automount', '5', [], ''], ['systemd.device', '5', [], ''], @@ -971,7 +1098,10 @@ manpages = [ ''], ['udev_new', '3', ['udev_ref', 'udev_unref'], ''], ['udevadm', '8', [], ''], - ['user@.service', '5', ['user-runtime-dir@.service'], ''], + ['user@.service', + '5', + ['systemd-user-runtime-dir', 'user-runtime-dir@.service'], + ''], ['userdbctl', '1', [], 'ENABLE_USERDB'], ['vconsole.conf', '5', [], 'ENABLE_VCONSOLE'] ] diff --git a/man/sd-bus-container-append.c b/man/sd-bus-container-append.c new file mode 100644 index 000000000..e350ea03d --- /dev/null +++ b/man/sd-bus-container-append.c @@ -0,0 +1,17 @@ +#include + +int append_strings_to_message(sd_bus_message *m, const char *const *arr) { + int r; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return r; + + for (const char *s = *arr; *s; s++) { + r = sd_bus_message_append(m, "s", s); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(m); +} diff --git a/man/sd-bus-container-read.c b/man/sd-bus-container-read.c new file mode 100644 index 000000000..b6c95f47f --- /dev/null +++ b/man/sd-bus-container-read.c @@ -0,0 +1,25 @@ +#include + +#include + +int read_strings_from_message(sd_bus_message *m) { + int r; + + r = sd_bus_message_enter_container(m, 'a', "s"); + if (r < 0) + return r; + + for (;;) { + const char *s; + + r = sd_bus_message_read(m, "s", &s); + if (r < 0) + return r; + if (r == 0) + break; + + printf("%s\n", s); + } + + return sd_bus_message_exit_container(m); +} diff --git a/man/sd-bus.xml b/man/sd-bus.xml index 76865e1f8..199a4a81e 100644 --- a/man/sd-bus.xml +++ b/man/sd-bus.xml @@ -41,37 +41,98 @@ See sd_bus_add_match3, +sd_bus_add_object3, +sd_bus_add_object_manager3, sd_bus_add_object_vtable3, +sd_bus_add_fallback3, +sd_bus_add_fallback_vtable3, +sd_bus_add_filter3, +sd_bus_add_node_enumerator3, sd_bus_attach_event3, +sd_bus_call3, +sd_bus_call_async3, +sd_bus_call_method3, +sd_bus_call_method_async3, +sd_bus_can_send3, sd_bus_creds_get_pid3, sd_bus_creds_new_from_pid3, sd_bus_close3, sd_bus_default3, +sd_bus_emit_interfaces_added3, +sd_bus_emit_interfaces_added_strv3, +sd_bus_emit_interfaces_removed3, +sd_bus_emit_interfaces_removed_strv3, +sd_bus_emit_object_added3, +sd_bus_emit_object_removed3, +sd_bus_emit_properties_changed3, +sd_bus_emit_properties_changed_strv3, +sd_bus_emit_signal3, +sd_bus_emit_signalv3, sd-bus-errors3, sd_bus_error3, sd_bus_error_add_map3, +sd_bus_get_address3, +sd_bus_get_allow_interactive_authorization3, +sd_bus_get_bus_id3, +sd_bus_get_creds_mask3, +sd_bus_get_current_handler3, +sd_bus_get_current_message3, +sd_bus_get_current_slot3, +sd_bus_get_current_userdata3, +sd_bus_get_exit_on_disconnect3, sd_bus_get_fd3, +sd_bus_get_method_call_timeout3, sd_bus_get_n_queued_read3, +sd_bus_get_name_machine_id3, +sd_bus_get_name_creds3, +sd_bus_get_owner_creds3, +sd_bus_get_property3, +sd_bus_get_property_trivial3, +sd_bus_get_property_string3, +sd_bus_get_property_strv3, +sd_bus_get_scope3, +sd_bus_get_tid3, +sd_bus_get_unique_name3, +sd_bus_interface_name_is_valid3, +sd_bus_is_monitor3, +sd_bus_is_bus_client3, +sd_bus_is_server3, +sd_bus_list_names3, sd_bus_message_append3, sd_bus_message_append_array3, sd_bus_message_append_basic3, sd_bus_message_append_string_memfd3, sd_bus_message_append_strv3, +sd_bus_message_at_end3, +sd_bus_message_close_container3, sd_bus_message_copy3, sd_bus_message_dump3, +sd_bus_message_enter_container3, +sd_bus_message_exit_container3, +sd_bus_message_get_allow_interactive_authorization3, sd_bus_message_get_cookie3, +sd_bus_message_get_creds3, +sd_bus_message_get_errno3, +sd_bus_message_get_error3, sd_bus_message_get_monotonic_usec3, +sd_bus_message_get_sender3, sd_bus_message_get_signature3, sd_bus_message_get_type3, sd_bus_message_new3, sd_bus_message_new_method_call3, sd_bus_message_new_method_error3, sd_bus_message_new_signal3, +sd_bus_message_open_container3, +sd_bus_message_peek_type3, sd_bus_message_read3, sd_bus_message_read_array3, sd_bus_message_read_basic3, +sd_bus_message_read_strv3, sd_bus_message_rewind3, +sd_bus_message_seal3, +sd_bus_message_set_allow_interactive_authorization3, sd_bus_message_set_destination3, +sd_bus_message_set_sender3, sd_bus_message_set_expect_reply3, sd_bus_message_skip3, sd_bus_message_verify_type3, @@ -79,17 +140,35 @@ sd_bus_new3, sd_bus_path_encode3, sd_bus_process3, +sd_bus_query_sender_creds3, +sd_bus_query_sender_privilege3, +sd_bus_reply_method_return3, sd_bus_reply_method_error3, sd_bus_request_name3, +sd_bus_send3, +sd_bus_send_to3, +sd_bus_set_address3, +sd_bus_set_allow_interactive_authorization3, +sd_bus_set_bus_client3, +sd_bus_set_close_on_exit3, sd_bus_set_connected_signal3, sd_bus_set_description3, +sd_bus_set_exit_on_disconnect3, +sd_bus_set_method_call_timeout3, +sd_bus_set_monitor3, +sd_bus_set_property3, +sd_bus_set_propertyv3, sd_bus_set_sender3, +sd_bus_set_server3, sd_bus_set_watch_bind3 -sd_bus_set_close_on_exit3 +sd_bus_slot_get_current_handler3, +sd_bus_slot_get_current_message3, +sd_bus_slot_get_current_userdata3, sd_bus_slot_set_description3, sd_bus_slot_set_destroy_callback3, sd_bus_slot_set_floating3, sd_bus_slot_set_userdata3, +sd_bus_start3, sd_bus_track_add_name3, sd_bus_track_new3 diff --git a/man/sd-hwdb.xml b/man/sd-hwdb.xml new file mode 100644 index 000000000..13552e584 --- /dev/null +++ b/man/sd-hwdb.xml @@ -0,0 +1,57 @@ + + + + + + + + sd-hwdb + systemd + + + + sd-hwdb + 3 + + + + sd-hwdb + Read-only access to the hardware description database + + + + + #include <systemd/sd-hwdb.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-hwdb.h allows read-only access the systemd database of hardware properties. + See hwdb7 and + systemd-hwdb8 for more + information about the database. + + See sd_hwdb_new3 + and sd_hwdb_get3 for + information about the functions available. + + + + + + See Also + + systemd1, + systemd-udevd.service8 + + + + diff --git a/man/sd-login.xml b/man/sd-login.xml index 2787bd747..ecc6e28a8 100644 --- a/man/sd-login.xml +++ b/man/sd-login.xml @@ -194,7 +194,7 @@ When set to 1, this device automatically generates a new and independent seat, which is named after the path of the - device. This is set for specialized USB hubs like the Plugable devices, which when + device. This is set for specialized USB hubs like the Pluggable devices, which when plugged in should create a hotplug seat without further configuration. diff --git a/man/sd_bus_add_match.xml b/man/sd_bus_add_match.xml index a10654438..071060dde 100644 --- a/man/sd_bus_add_match.xml +++ b/man/sd_bus_add_match.xml @@ -33,7 +33,7 @@ #include <systemd/sd-bus.h> - + typedef int (*sd_bus_message_handler_t) sd_bus_message *m void *userdata diff --git a/man/sd_bus_add_node_enumerator.xml b/man/sd_bus_add_node_enumerator.xml new file mode 100644 index 000000000..fd11e46fc --- /dev/null +++ b/man/sd_bus_add_node_enumerator.xml @@ -0,0 +1,137 @@ + + + + + + + + sd_bus_add_node_enumerator + systemd + + + + sd_bus_add_node_enumerator + 3 + + + + sd_bus_add_node_enumerator + + Add a node enumerator for a D-Bus object path prefix + + + + + #include <systemd/sd-bus.h> + + + typedef int (*sd_bus_node_enumerator_t) + sd_bus *bus + const char *prefix + void *userdata + char ***ret_nodes + sd_bus_error *ret_error + + + + int sd_bus_add_node_enumerator + sd_bus *bus + sd_bus_slot **slot + const char *path + sd_bus_node_enumerator_t callback + void *userdata + + + + + + Description + + sd_bus_add_node_enumerator() adds a D-Bus node enumerator for the + given path prefix. The given callback is called to enumerate all the available objects with + the given path prefix when required (e.g. when + org.freedesktop.DBus.Introspectable.Introspect or + org.freedesktop.DBus.ObjectManager.GetManagedObjects are called on a + D-Bus service managed by sd-bus). + + callback is called with the path and userdata pointer registered + with sd_bus_add_node_enumerator(). When called, it should store all the + child object paths of the given path prefix in ret_nodes and return the + number of child objects under the given prefix. If an error occurs, it can either return a + negative integer, set ret_error to a non-empty error or do both. Any + errors returned by the callback are encoded as D-Bus errors and sent back to the caller. Errors + in ret_error take priority over negative return values. + + Note that a node enumerator callback will only ever be called for a single path prefix + and hence, for normal operation, prefix can be ignored. Also, a node + enumerator is only used to enumerate the available child objects under a given prefix. To + install a handler for a set of dynamic child objects, use + sd_bus_add_fallback_vtable3. + + + When sd_bus_add_node_enumerator() succeeds, a slot is created + internally. If the output parameter slot is NULL, + a "floating" slot object is created, see + sd_bus_slot_set_floating3. + Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot + object should be dropped when the node enumerator is not needed anymore, see + sd_bus_slot_unref3. + + + + + Return Value + + On success, sd_bus_add_node_enumerator() returns a non-negative + integer. On failure, it returns a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + One of the required parameters is NULL or + path is not a valid object path. + + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + sd-bus3, + busctl1, + sd_bus_add_fallback_vtable3, + sd_bus_slot_unref3 + + + diff --git a/man/sd_bus_add_object.xml b/man/sd_bus_add_object.xml new file mode 100644 index 000000000..bce71bd11 --- /dev/null +++ b/man/sd_bus_add_object.xml @@ -0,0 +1,651 @@ + + + + + + + + sd_bus_add_object + systemd + + + + sd_bus_add_object + 3 + + + + sd_bus_add_object + sd_bus_add_fallback + sd_bus_add_object_vtable + sd_bus_add_fallback_vtable + sd_bus_add_filter + SD_BUS_VTABLE_START + SD_BUS_VTABLE_END + SD_BUS_METHOD_WITH_NAMES_OFFSET + SD_BUS_METHOD_WITH_NAMES + SD_BUS_METHOD_WITH_OFFSET + SD_BUS_METHOD + SD_BUS_SIGNAL_WITH_NAMES + SD_BUS_SIGNAL + SD_BUS_WRITABLE_PROPERTY + SD_BUS_PROPERTY + SD_BUS_PARAM + + Declare properties and methods for a D-Bus path + + + + + #include <systemd/sd-bus-vtable.h> + + + + + typedef int (*sd_bus_property_get_t) + sd_bus *bus + const char *path + const char *interface + const char *property + sd_bus_message *reply + void *userdata + sd_bus_error *ret_error + + + + typedef int (*sd_bus_property_set_t) + sd_bus *bus + const char *path + const char *interface + const char *property + sd_bus_message *value + void *userdata + sd_bus_error *ret_error + + + + typedef int (*sd_bus_object_find_t) + const char *path + const char *interface + void *userdata + void **ret_found + sd_bus_error *ret_error + + + + int sd_bus_add_object + sd_bus *bus + sd_bus_slot **slot + const char *path + sd_bus_message_handler_t callback + void *userdata + + + + int sd_bus_add_fallback + sd_bus *bus + sd_bus_slot **slot + const char *path + sd_bus_message_handler_t callback + void *userdata + + + + int sd_bus_add_object_vtable + sd_bus *bus + sd_bus_slot **slot + const char *path + const char *interface + const sd_bus_vtable *vtable + void *userdata + + + + int sd_bus_add_fallback_vtable + sd_bus *bus + sd_bus_slot **slot + const char *prefix + const char *interface + const sd_bus_vtable *vtable + sd_bus_object_find_t find + void *userdata + + + + int sd_bus_add_filter + sd_bus *bus + sd_bus_slot **slot + sd_bus_message_handler_t callback + void *userdata + + + + SD_BUS_VTABLE_START(flags) + + + SD_BUS_VTABLE_END + + + SD_BUS_METHOD_WITH_ARGS_OFFSET( + member, + args, + result, + handler, + offset, + flags) + + + + SD_BUS_METHOD_WITH_ARGS( + member, + args, + result, + handler, + flags) + + + + SD_BUS_METHOD_WITH_NAMES_OFFSET( + member, + signature, + in_names, + result, + out_names, + handler, + offset, + flags) + + + + SD_BUS_METHOD_WITH_NAMES( + member, + signature, + in_names, + result, + out_names, + handler, + flags) + + + + SD_BUS_METHOD_WITH_OFFSET( + member, + signature, + result, + handler, + offset, + flags) + + + + SD_BUS_METHOD( + member, + signature, + result, + handler, + flags) + + + + SD_BUS_SIGNAL_WITH_ARGS( + member, + args, + flags) + + + + SD_BUS_SIGNAL_WITH_NAMES( + member, + signature, + names, + flags) + + + + SD_BUS_SIGNAL( + member, + signature, + flags) + + + + SD_BUS_WRITABLE_PROPERTY( + member, + signature, + get, + set, + offset, + flags) + + + + SD_BUS_PROPERTY( + member, + signature, + get, + offset, + flags) + + + + SD_BUS_PARAM(name) + SD_BUS_ARGS(...) + SD_BUS_RESULT(...) + SD_BUS_NO_ARGS + SD_BUS_NO_RESULT + + + + + + Description + + sd_bus_add_object_vtable() is used to declare attributes for the + object path path connected to the bus connection + bus under the interface interface. The table + vtable may contain property declarations using + SD_BUS_PROPERTY() or SD_BUS_WRITABLE_PROPERTY(), + method declarations using SD_BUS_METHOD(), + SD_BUS_METHOD_WITH_NAMES(), + SD_BUS_METHOD_WITH_OFFSET(), or + SD_BUS_METHOD_WITH_NAMES_OFFSET(), and signal declarations using + SD_BUS_SIGNAL_WITH_NAMES() or SD_BUS_SIGNAL(), see + below. The userdata parameter contains a pointer that will be passed + to various callback functions. It may be specified as NULL if no value is + necessary. An interface can have any number of vtables attached to it. + + sd_bus_add_fallback_vtable() is similar to + sd_bus_add_object_vtable(), but is used to register "fallback" attributes. + When looking for an attribute declaration, bus object paths registered with + sd_bus_add_object_vtable() are checked first. If no match is found, the + fallback vtables are checked for each prefix of the bus object path, i.e. with the last + slash-separated components successively removed. This allows the vtable to be used for an + arbitrary number of dynamically created objects. + + Parameter find is a function which is used to locate the target + object based on the bus object path path. It must return + 1 and set the ret_found output parameter if the + object is found, return 0 if the object was not found, and return a + negative errno-style error code or initialize the error structure + ret_error on error. The pointer passed in + ret_found will be used as the userdata parameter + for the callback functions (offset by the offset offsets as specified in + the vtable entries). + + sd_bus_add_object() attaches a callback directly to the object path + path. An object path can have any number of callbacks attached to it. + Each callback is prepended to the list of callbacks which are always called in order. + sd_bus_add_fallback() is similar to + sd_bus_add_object() but applies to fallback paths instead. + + sd_bus_add_filter() installs a callback that is invoked for each + incoming D-Bus message. Filters can be used to handle logic common to all messages received by + a service (e.g. authentication or authorization). + + When a request is received, any associated callbacks are called sequentially until a + callback returns a non-zero integer. Return zero from a callback to give other callbacks the + chance to process the request. Callbacks are called in the following order: first, global + callbacks installed with sd_bus_add_filter() are called. Second, callbacks + attached directly to the request object path are called, followed by any D-Bus method callbacks + attached to the request object path, interface and member. Finally, the property callbacks + attached to the request object path, interface and member are called. If the final callback + returns zero, an error reply is sent back to the caller indicating no matching object for the + request was found. Note that you can return a positive integer from a callback without + immediately sending a reply. This informs sd-bus this callback will take responsibility for + replying to the request without forcing the callback to produce a reply immediately. This allows + a callback to perform any number of asynchronous operations required to construct a reply. Note + that if producing a reply takes too long, the method call will time out at the caller. + + If a callback was invoked to handle a request that expects a reply and the callback + returns a negative value, the value is interpreted as a negative errno-style error code and sent + back to the caller as a D-Bus error as if + sd_bus_reply_method_errno3 + was called. Additionally, all callbacks take a sd_bus_error output + parameter that can be used to provide more detailed error information. If + ret_error is set when the callback finishes, the corresponding D-Bus + error is sent back to the caller as if + sd_bus_reply_method_error3 + was called. Any error stored in ret_error takes priority over any + negative values returned by the same callback when determining which error to send back to + the caller. Use + sd_bus_error_set3 + or one of its variants to set ret_error and return a negative integer + from a callback with a single function call. To send an error reply after a callback has already + finished, use + sd_bus_reply_method_errno3 + or one of its variants. + + For all functions, a match slot is created internally. If the output parameter + slot is NULL, a "floating" slot object is + created, see + sd_bus_slot_set_floating3. + Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot + object should be dropped when the vtable is not needed anymore, see + sd_bus_slot_unref3. + + + + The <structname>sd_bus_vtable</structname> array + + The array consists of the structures of type sd_bus_vtable, but it + should never be filled in manually, but through one of the following macros: + + + + SD_BUS_VTABLE_START() + SD_BUS_VTABLE_END + + Those must always be the first and last element. + + + + SD_BUS_METHOD_WITH_ARGS_OFFSET() + SD_BUS_METHOD_WITH_ARGS() + + Declare a D-Bus method with the name member, + arguments args and result result. + args expects a sequence of argument type/name pairs wrapped in the + SD_BUS_ARGS() macro. The elements at even indices in this list describe the + types of the method's arguments. The method's parameter signature is the concatenation of all the + string literals at even indices in args. If a method has no parameters, + pass SD_BUS_NO_ARGS to args. The elements at uneven + indices describe the names of the method's arguments. result expects a + sequence of type/name pairs wrapped in the SD_BUS_RESULT() macro in the same + format as SD_BUS_ARGS(). The method's result signature is the concatenation of + all the string literals at even indices in result. If a method has no + result, pass SD_BUS_NO_RESULT to result. Note that + argument types are expected to be quoted string literals and argument names are expected to be + unquoted string literals. See below for a complete example. + + The handler function handler must be of type + sd_bus_message_handler_t. It will be called to handle the incoming messages + that call this method. It receives a pointer that is the userdata + parameter passed to the registration function offset by offset bytes. + This may be used to pass pointers to different fields in the same data structure to different + methods in the same vtable. To send a reply from handler, call + sd_bus_reply_method_return3 + with the message the callback was invoked with. Parameter flags is a + combination of flags, see below. + + SD_BUS_METHOD_WITH_ARGS() is a shorthand for calling + SD_BUS_METHOD_WITH_ARGS_OFFSET() with an offset of zero. + + + + + SD_BUS_METHOD_WITH_NAMES_OFFSET() + SD_BUS_METHOD_WITH_NAMES() + SD_BUS_METHOD_WITH_OFFSET() + SD_BUS_METHOD() + + Declare a D-Bus method with the name member, + parameter signature signature, result signature + result. Parameters in_names and + out_names specify the argument names of the input and output + arguments in the function signature. in_names and + out_names should be created using the + SD_BUS_PARAM() macro, see below. In all other regards, this macro behaves + exactly the same as SD_BUS_METHOD_WITH_ARGS_OFFSET(). + + SD_BUS_METHOD_WITH_NAMES(), + SD_BUS_METHOD_WITH_OFFSET(), and SD_BUS_METHOD() + are variants which specify zero offset (userdata parameter is + passed with no change), leave the names unset (i.e. no parameter names), or both. + + Prefer using SD_BUS_METHOD_WITH_ARGS_OFFSET() and + SD_BUS_METHOD_WITH_ARGS() over these macros as they allow specifying argument + types and names next to each other which is less error-prone than first specifying all argument + types followed by specifying all argument names. + + + + + SD_BUS_SIGNAL_WITH_ARGS() + + >Declare a D-Bus signal with the name member and + arguments args. args expects a sequence of + argument type/name pairs wrapped in the SD_BUS_ARGS() macro. The elements at + even indices in this list describe the types of the signal's arguments. The signal's parameter + signature is the concatenation of all the string literals at even indices in + args. If a signal has no parameters, pass + SD_BUS_NO_ARGS to args. The elements at uneven + indices describe the names of the signal's arguments. Parameter flags is + a combination of flags. See below for a complete example. + + + + SD_BUS_SIGNAL_WITH_NAMES() + SD_BUS_SIGNAL() + + Declare a D-Bus signal with the name member, + parameter signature signature, and argument names + names. names should be + created using the SD_BUS_PARAM() macro, see below. + Parameter flags is a combination of flags, see below. + + + SD_BUS_SIGNAL() is equivalent to + SD_BUS_SIGNAL_WITH_NAMES() with the names parameter + unset (i.e. no parameter names). + + Prefer using SD_BUS_SIGNAL_WITH_ARGS() over these macros as it allows + specifying argument types and names next to each other which is less error-prone than first + specifying all argument types followed by specifying all argument names. + + + + + SD_BUS_WRITABLE_PROPERTY() + SD_BUS_PROPERTY() + + Declare a D-Bus property with the name member + and value signature signature. Parameters + get and set are the getter and + setter methods. They are called with a pointer that is the + userdata parameter passed to the registration function offset + by offset bytes. This may be used pass pointers to different + fields in the same data structure to different setters and getters in the same vtable. + Parameter flags is a combination of flags, see below. + + The setter and getter methods may be omitted (specified as + NULL), if the property is one of the basic types or + as in case of read-only properties. In those cases, the + userdata and offset parameters must + together point to a valid variable of the corresponding type. A default setter and getter + will be provided, which simply copy the argument between this variable and the message. + + + SD_BUS_PROPERTY() is used to define a read-only property. + + + + + SD_BUS_PARAM() + Parameter names should be wrapped in this macro, see the example below. + + + + + + + Flags + + The flags parameter is used to specify a combination of + D-Bus annotations. + + + + + SD_BUS_VTABLE_DEPRECATED + + Mark this vtable entry as deprecated using the + org.freedesktop.DBus.Deprecated annotation in introspection data. If + specified for SD_BUS_VTABLE_START(), the annotation is applied to the + enclosing interface. + + + + SD_BUS_VTABLE_HIDDEN + + Make this vtable entry hidden. It will not be shown in introspection data. + If specified for SD_BUS_VTABLE_START(), all entries in the array are + hidden. + + + + SD_BUS_VTABLE_UNPRIVILEGED + + Mark this vtable entry as unprivileged. If not specified, the + org.freedesktop.systemd1.Privileged annotation with value + true will be shown in introspection data. + + + + SD_BUS_VTABLE_METHOD_NO_REPLY + + Mark his vtable entry as a method that will not return a reply using the + org.freedesktop.DBus.Method.NoReply annotation in introspection data. + + + + + SD_BUS_VTABLE_PROPERTY_CONST + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION + + Those three flags correspond to different values of the + org.freedesktop.DBus.Property.EmitsChangedSignal annotation, which + specifies whether the + org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted + whenever the property changes. SD_BUS_VTABLE_PROPERTY_CONST + corresponds to const and means that the property never changes during + the lifetime of the object it belongs to, so no signal needs to be emitted. + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE corresponds to + true and means that the signal is emitted. + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION corresponds to + invalidates and means that the signal is emitted, but the value is + not included in the signal. + + + + SD_BUS_VTABLE_PROPERTY_EXPLICIT + + Mark this vtable property entry as requiring explicit request to for the + value to be shown (generally because the value is large or slow to calculate). This entry + cannot be combined with SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, and will + not be shown in property listings by default (e.g. busctl introspect). + This corresponds to the org.freedesktop.systemd1.Explicit annotation + in introspection data. + + + + SD_BUS_VTABLE_SENSITIVE + + Mark this vtable method entry as processing sensitive data. When set, + incoming method call messages and their outgoing reply messages are marked as sensitive using + sd_bus_message_sensitive3, + so that they are erased from memory when freed. + + + + SD_BUS_VTABLE_ABSOLUTE_OFFSET + + Mark this vtable method or property entry so that the user data pointer passed to + its associated handler functions is determined slightly differently: instead of adding the offset + parameter of the entry to the user data pointer specified during vtable registration, the offset is + passed directly, converted to a pointer, without taking the user data pointer specified during + vtable registration into account. + + + + + + + Examples + + + Create a simple listener on the bus + + + + This creates a simple client on the bus (the user bus, when run as normal user). We may + use the D-Bus org.freedesktop.DBus.Introspectable.Introspect call to + acquire the XML description of the interface: + + + + + + + Return Value + + On success, sd_bus_add_object_vtable() and + sd_bus_add_fallback_vtable() return a non-negative integer. On + failure, they return a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + One of the required parameters is NULL or invalid. A + reserved D-Bus interface was passed as the interface parameter. + + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + -EPROTOTYPE + + sd_bus_add_object_vtable and + sd_bus_add_fallback_vtable have been both called for the same bus + object path, which is not allowed. + + + + -EEXIST + + This vtable has already been registered for this + interface and path. + + + + + + + + + + See Also + + + sd-bus3, + busctl1, + sd_bus_emit_properties_changed3, + sd_bus_emit_object_added3 + + + diff --git a/man/sd_bus_add_object_manager.xml b/man/sd_bus_add_object_manager.xml new file mode 100644 index 000000000..cc442d161 --- /dev/null +++ b/man/sd_bus_add_object_manager.xml @@ -0,0 +1,118 @@ + + + + + + + + sd_bus_add_object_manager + systemd + + + + sd_bus_add_object_manager + 3 + + + + sd_bus_add_object_manager + + Add a D-Bus object manager for a D-Bus object sub-tree + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_add_object_manager + sd_bus *bus + sd_bus_slot **slot + const char *path + + + + + + Description + + sd_bus_add_object_manager() installs a handler for the given path + that implements the GetManagedObjects() method of the + org.freedesktop.DBus.ObjectManager interface. See + + org.freedesktop.DBus.ObjectManager for more information. + + To implement the InterfacesAdded and + InterfacesRemoved signals of the + org.freedesktop.DBus.ObjectManager interface, call + sd_bus_emit_interfaces_added3 and + sd_bus_emit_interfaces_removed3 + whenever interfaces are added or removed from the sub-tree, respectively. + + When sd_bus_add_object_manager() succeeds, a slot is created + internally. If the output parameter slot is NULL, + a "floating" slot object is created, see + sd_bus_slot_set_floating3. + Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot + object should be dropped when the object manager is not needed anymore, see + sd_bus_slot_unref3. + + + + + Return Value + + On success, sd_bus_add_object_manager() returns a non-negative + integer. On failure, it returns a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + One of the required parameters is NULL or + path is not a valid object path. + + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + sd-bus3, + busctl1, + sd_bus_add_object_vtable3, + sd_bus_emit_interfaces_added3, + sd_bus_slot_unref3 + + + diff --git a/man/sd_bus_add_object_vtable.xml b/man/sd_bus_add_object_vtable.xml deleted file mode 100644 index 9d7e30a50..000000000 --- a/man/sd_bus_add_object_vtable.xml +++ /dev/null @@ -1,473 +0,0 @@ - - - - - - - - sd_bus_add_object_vtable - systemd - - - - sd_bus_add_object_vtable - 3 - - - - sd_bus_add_object_vtable - sd_bus_add_fallback_vtable - SD_BUS_VTABLE_START - SD_BUS_VTABLE_END - SD_BUS_METHOD_WITH_NAMES_OFFSET - SD_BUS_METHOD_WITH_NAMES - SD_BUS_METHOD_WITH_OFFSET - SD_BUS_METHOD - SD_BUS_SIGNAL_WITH_NAMES - SD_BUS_SIGNAL - SD_BUS_WRITABLE_PROPERTY - SD_BUS_PROPERTY - SD_BUS_PARAM - - Declare properties and methods for a D-Bus path - - - - - #include <systemd/sd-bus-vtable.h> - - - typedef int (*sd_bus_message_handler_t) - sd_bus_message *m - void *userdata - sd_bus_error *ret_error - - - - typedef int (*sd_bus_property_get_t) - sd_bus *bus - const char *path - const char *interface - const char *property - sd_bus_message *reply - void *userdata - sd_bus_error *ret_error - - - - typedef int (*sd_bus_property_set_t) - sd_bus *bus - const char *path - const char *interface - const char *property - sd_bus_message *value - void *userdata - sd_bus_error *ret_error - - - - typedef int (*sd_bus_object_find_t) - const char *path - const char *interface - void *userdata - void **ret_found - sd_bus_error *ret_error - - - - int sd_bus_add_object_vtable - sd_bus *bus - sd_bus_slot **slot - const char *path - const char *interface - const sd_bus_vtable *vtable - void *userdata - - - - int sd_bus_add_fallback_vtable - sd_bus *bus - sd_bus_slot **slot - const char *prefix - const char *interface - const sd_bus_vtable *vtable - sd_bus_object_find_t find - void *userdata - - - - SD_BUS_VTABLE_START(flags) - - - SD_BUS_VTABLE_END - - - SD_BUS_METHOD_WITH_NAMES_OFFSET( - member, - signature, - in_names, - result, - out_names, - handler, - offset, - flags) - - - - SD_BUS_METHOD_WITH_NAMES( - member, - signature, - in_names, - result, - out_names, - handler, - flags) - - - - SD_BUS_METHOD_WITH_OFFSET( - member, - signature, - result, - handler, - offset, - flags) - - - - SD_BUS_METHOD( - member, - signature, - result, - handler, - flags) - - - - SD_BUS_SIGNAL_WITH_NAMES( - member, - signature, - names, - flags) - - - - SD_BUS_SIGNAL( - member, - signature, - flags) - - - - SD_BUS_WRITABLE_PROPERTY( - member, - signature, - get, - set, - offset, - flags) - - - - SD_BUS_PROPERTY( - member, - signature, - get, - offset, - flags) - - - - SD_BUS_PARAM(name) - - - - - - Description - - sd_bus_add_object_vtable() is used to declare attributes for the path object - path path connected to the bus connection bus under the - interface interface. The table vtable may contain property - declarations using SD_BUS_PROPERTY() or - SD_BUS_WRITABLE_PROPERTY(), method declarations using - SD_BUS_METHOD(), SD_BUS_METHOD_WITH_NAMES(), - SD_BUS_METHOD_WITH_OFFSET(), or - SD_BUS_METHOD_WITH_NAMES_OFFSET(), and signal declarations using - SD_BUS_SIGNAL_WITH_NAMES() or SD_BUS_SIGNAL(), see below. The - userdata parameter contains a pointer that will be passed to various callback - functions. It may be specified as NULL if no value is necessary. - - sd_bus_add_fallback_vtable() is similar to - sd_bus_add_object_vtable(), but is used to register "fallback" attributes. When - looking for an attribute declaration, bus object paths registered with - sd_bus_add_object_vtable() are checked first. If no match is found, the fallback - vtables are checked for each prefix of the bus object path, i.e. with the last slash-separated components - successively removed. This allows the vtable to be used for an arbitrary number of dynamically created - objects. - - Parameter find is a function which is used to locate the target object - based on the bus object path path. It must return 1 and - set the ret_found output parameter if the object is found, return - 0 if the object was not found, and return a negative errno-style error code or - initialize the error structure ret_error on error. The pointer passed in - ret_found will be used as the userdata parameter for the - callback functions (offset by the offset offsets as specified in the vtable - entries). - - For both functions, a match slot is created internally. If the output parameter - slot is NULL, a "floating" slot object is created, see - sd_bus_slot_set_floating3. - Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot object - should be dropped when the vtable is not needed anymore, see - sd_bus_slot_unref3. - - - - The <structname>sd_bus_vtable</structname> array - - The array consists of the structures of type sd_bus_vtable, but it - should never be filled in manually, but through one of the following macros: - - - - SD_BUS_VTABLE_START() - SD_BUS_VTABLE_END - - Those must always be the first and last element. - - - - SD_BUS_METHOD_WITH_NAMES_OFFSET() - SD_BUS_METHOD_WITH_NAMES() - SD_BUS_METHOD_WITH_OFFSET() - SD_BUS_METHOD() - - Declare a D-Bus method with the name member, parameter - signature signature, result signature result. - Parameters in_names and out_names specify the - argument names of the input and output arguments in the function signature. The handler function - handler must be of type sd_bus_message_handler_t. - It will be called to handle the incoming messages that call this method. It receives a pointer that - is the userdata parameter passed to the registration function offset by - offset bytes. This may be used to pass pointers to different fields in - the same data structure to different methods in the same - vtable. in_names and out_names should be - created using the SD_BUS_PARAM() macro, see below. Parameter - flags is a combination of flags, see below. - - SD_BUS_METHOD_WITH_NAMES(), - SD_BUS_METHOD_WITH_OFFSET(), and SD_BUS_METHOD() are - variants which specify zero offset (userdata parameter is passed with - no change), leave the names unset (i.e. no parameter names), or both. - - - - - SD_BUS_SIGNAL_WITH_NAMES() - SD_BUS_SIGNAL() - - Declare a D-Bus signal with the name member, - parameter signature signature, and argument names - names. names should be - created using the SD_BUS_PARAM() macro, see below. - Parameter flags is a combination of flags, see below. - - - Equivalent to SD_BUS_SIGNAL_WITH_NAMES() with the - names parameter unset (i.e. no parameter names). - - - - - SD_BUS_WRITABLE_PROPERTY() - SD_BUS_PROPERTY() - - Declare a D-Bus property with the name member and value - signature signature. Parameters get and - set are the getter and setter methods. They are called with a pointer - that is the userdata parameter passed to the registration function - offset by offset bytes. This may be used pass pointers to different - fields in the same data structure to different setters and getters in the same vtable. Parameter - flags is a combination of flags, see below. - - The setter and getter methods may be omitted (specified as NULL), if the - property has one of the basic types or as in case of read-only properties. In - those cases, the userdata and offset - parameters must together point to valid variable of the corresponding type. A default setter and - getters will be provided, which simply copy the argument between this variable and the message. - - - SD_BUS_PROPERTY() is used to define a read-only property. - - - - - SD_BUS_PARAM() - Parameter names should be wrapped in this macro, see the example below. - - - - - - - Flags - - The flags parameter is used to specify a combination of - D-Bus annotations. - - - - - SD_BUS_VTABLE_DEPRECATED - - Mark this vtable entry as deprecated using the - org.freedesktop.DBus.Deprecated annotation in introspection data. If - specified for SD_BUS_VTABLE_START(), the annotation is applied to the - enclosing interface. - - - - SD_BUS_VTABLE_HIDDEN - - Make this vtable entry hidden. It will not be shown in introspection data. If - specified for SD_BUS_VTABLE_START(), all entries in the array are hidden. - - - - - - SD_BUS_VTABLE_UNPRIVILEGED - - Mark this vtable entry as unprivileged. If not specified, the - org.freedesktop.systemd1.Privileged annotation with value - true will be shown in introspection data. - - - - - SD_BUS_VTABLE_METHOD_NO_REPLY - - Mark his vtable entry as a method that will not return a reply using the - org.freedesktop.DBus.Method.NoReply annotation in introspection data. - - - - - SD_BUS_VTABLE_PROPERTY_CONST - SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE - SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION - - Those three flags correspond to different values of the - org.freedesktop.DBus.Property.EmitsChangedSignal annotation, which specifies - whether the org.freedesktop.DBus.Properties.PropertiesChanged signal is - emitted whenever the property changes. SD_BUS_VTABLE_PROPERTY_CONST corresponds to - const and means that the property never changes during the lifetime of the - object it belongs to, so no signal needs to be emitted. - SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE corresponds to true and means - that the signal is emitted. SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION corresponds to - invalidates and means that the signal is emitted, but the value is not included - in the signal. - - - - - SD_BUS_VTABLE_PROPERTY_EXPLICIT - - Mark this vtable property entry as requiring explicit request to for the value to - be shown (generally because the value is large or slow to calculate). This entry cannot be combined - with SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, and will not be shown in property listings by - default (e.g. busctl introspect). This corresponds to the - org.freedesktop.systemd1.Explicit annotation in introspection data. - - - - - - - - Examples - - - Create a simple listener on the bus - - - - This creates a simple client on the bus (the user bus, when run as normal user). - We may use the D-Bus org.freedesktop.DBus.Introspectable.Introspect - call to acquire the XML description of the interface: - - - - - - - Return Value - - On success, sd_bus_add_object_vtable and - sd_bus_add_fallback_vtable calls return 0 or a positive integer. On failure, they - return a negative errno-style error code. - - - Errors - - Returned errors may indicate the following problems: - - - - -EINVAL - - One of the required parameters is NULL or invalid. A reserved - D-Bus interface was passed as the interface parameter. - - - - -ENOPKG - - The bus cannot be resolved. - - - - -ECHILD - - The bus was created in a different process. - - - - -ENOMEM - - Memory allocation failed. - - - - -EPROTOTYPE - - sd_bus_add_object_vtable and - sd_bus_add_fallback_vtable have been both called - for the same bus object path, which is not allowed. - - - - -EEXIST - - This vtable has already been registered for this - interface and path. - - - - - - - - - - See Also - - - sd-bus3, - busctl1 - - - diff --git a/man/sd_bus_call.xml b/man/sd_bus_call.xml new file mode 100644 index 000000000..f47f9c852 --- /dev/null +++ b/man/sd_bus_call.xml @@ -0,0 +1,187 @@ + + + + + + + + sd_bus_call + systemd + + + + sd_bus_call + 3 + + + + sd_bus_call + sd_bus_call_async + + Invoke a D-Bus method call + + + + + #include <systemd/sd-bus.h> + + + + + int sd_bus_call + sd_bus *bus + sd_bus_message *m + uint64_t usec + sd_bus_error *ret_error + sd_bus_message **reply + + + + int sd_bus_call_async + sd_bus *bus + sd_bus_slot **slot + sd_bus_message *m + sd_bus_message_handler_t callback + void *userdata + uint64_t usec + + + + + + Description + + sd_bus_call() takes a complete bus message object and calls the + corresponding D-Bus method. On success, the response is stored in reply. + usec indicates the timeout in microseconds. If + ret_error is not NULL and + sd_bus_call() fails (either because of an internal error or because it + received a D-Bus error reply), ret_error is initialized to an instance of + sd_bus_error describing the error. + + sd_bus_call_async() is like sd_bus_call() but works + asynchronously. The callback indicates the function to call when the response + arrives. The userdata pointer will be passed to the callback function, and may be + chosen freely by the caller. If slot is not NULL and + sd_bus_call_async() succeeds, slot is set to a slot object + which can be used to cancel the method call at a later time using + sd_bus_slot_unref3. + If slot is NULL, the lifetime of the method call is bound to + the lifetime of the bus object itself, and it cannot be cancelled independently. See + sd_bus_slot_set_floating3 + for details. callback is called when a reply arrives with the reply, + userdata and an sd_bus_error output parameter as its + arguments. Unlike sd_bus_call(), the sd_bus_error output + parameter passed to the callback will be empty. To determine whether the method call succeeded, use + sd_bus_message_is_method_error3 + on the reply message passed to the callback instead. If the callback returns zero and the + sd_bus_error output parameter is still empty when the callback finishes, other + handlers registered with functions such as + sd_bus_add_filter3 or + sd_bus_add_match3 are + given a chance to process the message. If the callback returns a non-zero value or the + sd_bus_error output parameter is not empty when the callback finishes, no + further processing of the message is done. Generally, you want to return zero from the callback to give + other registered handlers a chance to process the reply as well. (Note that the + sd_bus_error parameter is an output parameter of the callback function, not an + input parameter; it can be used to propagate errors from the callback handler, it will not receive any + error that was received as method reply.) + + If usec is zero, the default D-Bus method call timeout is used. See + sd_bus_get_method_call_timeout3. + + + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The input parameter m is NULL. + + + The input parameter m is not a D-Bus method call. + To create a new D-Bus method call, use + sd_bus_message_new_method_call3. + + + The input parameter m has the + BUS_MESSAGE_NO_REPLY_EXPECTED flag set. + + The input parameter error is + non-NULL but was not set to SD_BUS_ERROR_NULL. + + + + + -ECHILD + + The bus connection was allocated in a parent process and is being reused + in a child process after fork(). + + + + -ENOTCONN + + The input parameter bus is + NULL or the bus is not connected. + + + + -ECONNRESET + + The bus connection was closed while waiting for the response. + + + + + -ETIMEDOUT + + A response was not received within the given timeout. + + + + -ELOOP + + The message m is addressed to its own client. + + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call_method3, + sd_bus_call_method_async3, + sd_bus_message_new_method_call3, + sd_bus_message_append3 + + + + diff --git a/man/sd_bus_call_method.xml b/man/sd_bus_call_method.xml new file mode 100644 index 000000000..ac9cf143a --- /dev/null +++ b/man/sd_bus_call_method.xml @@ -0,0 +1,143 @@ + + + + + + + + sd_bus_call_method + systemd + + + + sd_bus_call_method + 3 + + + + sd_bus_call_method + sd_bus_call_methodv + sd_bus_call_method_async + sd_bus_call_method_asyncv + + Initialize a bus message object and invoke the corresponding D-Bus method call + + + + + + #include <systemd/sd-bus.h> + + + + + int sd_bus_call_method + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + sd_bus_message **reply + const char *types + ... + + + + int sd_bus_call_methodv + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + sd_bus_message **reply + const char *types + va_list ap + + + + int sd_bus_call_method_async + sd_bus *bus + sd_bus_slot **slot + const char *destination + const char *path + const char *interface + const char *member + sd_bus_message_handler_t callback + void *userdata + const char *types + ... + + + + int sd_bus_call_method_asyncv + sd_bus *bus + sd_bus_slot **slot + const char *destination + const char *path + const char *interface + const char *member + sd_bus_message_handler_t callback + void *userdata + const char *types + va_list ap + + + + + + Description + + sd_bus_call_method() is a convenience function for initializing a + bus message object and calling the corresponding D-Bus method. It combines the + sd_bus_message_new_method_call3, + sd_bus_message_append3 and + sd_bus_call3 + functions into a single function call. + + sd_bus_call_method_async() is a convenience function for initializing + a bus message object and calling the corresponding D-Bus method asynchronously. It combines the + sd_bus_message_new_method_call3, + sd_bus_message_append3 and + sd_bus_call_async3 + functions into a single function call. + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. + + + Errors + + See the man pages of + sd_bus_message_new_method_call3, + sd_bus_message_append3, + sd_bus_call3 and + sd_bus_call_async3 + for a list of possible errors. + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_new_method_call3, + sd_bus_message_append3, + sd_bus_call3, + sd_bus_set_property3, + sd_bus_emit_signal3 + + + + diff --git a/man/sd_bus_can_send.xml b/man/sd_bus_can_send.xml new file mode 100644 index 000000000..ba2a180ee --- /dev/null +++ b/man/sd_bus_can_send.xml @@ -0,0 +1,93 @@ + + + + + + + + sd_bus_can_send + systemd + + + + sd_bus_can_send + 3 + + + + sd_bus_can_send + + Check which types can be sent over a bus object + + + + + #include <systemd/sd-bus.h> + + + void sd_bus_can_send + sd_bus *bus + char type + + + + + + Description + + sd_bus_can_send is mostly used for checking if file descriptor + passing is available on the given bus. type can be any of the + SD_BUS_TYPE constants. + + + + Return Value + + On failure, sd_bus_can_send() returns a negative errno-style error + code. If values of the given type can be sent over the given bus, it returns a positive integer. + Otherwise, it returns zero. + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOPKG + + The bus object bus could not be resolved. + + + + + -ENOTCONN + + The input parameter bus is + NULL or the bus is not connected. + + + + -ECHILD + + The bus object bus was created in a different + process. + + + + + + + + + See Also + + + systemd1, + sd-bus3 + + + + diff --git a/man/sd_bus_close.xml b/man/sd_bus_close.xml index d81c59387..42db10747 100644 --- a/man/sd_bus_close.xml +++ b/man/sd_bus_close.xml @@ -19,6 +19,7 @@ sd_bus_close sd_bus_flush + sd_bus_default_flush_close Close and flush a bus connection @@ -36,36 +37,55 @@ int sd_bus_flush sd_bus *bus + + + void sd_bus_default_flush_close + void + Description - sd_bus_close() disconnects the specified bus connection. When this call is invoked and - the specified bus object refers to an active connection it is immediately terminated. No further messages may be - sent or received on it. Any messages queued in the bus object (both incoming and outgoing) are released. If - invoked on NULL bus object or when the bus connection is already closed this function executes - no operation. This call does not free or unreference the bus object itself. Use - sd_bus_unref3 for that. + sd_bus_close() disconnects the specified bus connection. When this + call is invoked and the specified bus object refers to an active connection it is immediately + terminated. No further messages may be sent or received on it. Any messages queued in the bus + object (both incoming and outgoing) are released. If invoked on NULL bus + object or when the bus connection is already closed this function executes no operation. This + call does not free or unreference the bus object itself. Use + sd_bus_unref3 + for that. - sd_bus_flush() synchronously writes out all outgoing queued message on a bus connection - if there are any. This function call may block if the peer is not processing bus messages quickly. + sd_bus_flush() synchronously writes out all outgoing queued message + on a bus connection if there are any. This function call may block if the peer is not processing + bus messages quickly. Before a program exits it is usually a good idea to flush any pending messages with - sd_bus_flush() and then close connections with sd_bus_close() to ensure - that no unwritten messages are lost, no further messages may be queued and all incoming but unprocessed messages - are released. After both operations have been done, it is a good idea to also drop any remaining references to the - bus object so that it may be freed. Since these three operations are frequently done together a helper call - sd_bus_flush_close_unref3 is - provided that combines them into one. + sd_bus_flush() and then close connections with + sd_bus_close() to ensure that no unwritten messages are lost, no further + messages may be queued and all incoming but unprocessed messages are released. After both + operations have been done, it is a good idea to also drop any remaining references to the bus + object so that it may be freed. Since these three operations are frequently done together a + helper call + sd_bus_flush_close_unref3 + is provided that combines them into one. + + sd_bus_default_flush_close() is similar to + sd_bus_flush_close_unref, but does not take a bus pointer argument and + instead iterates over any of the "default" buses opened by + sd_bus_default3, + sd_bus_default_user3, + sd_bus_default_system3, + and similar calls. sd_bus_default_flush_close() is particularly useful to + clean up any buses opened using those calls before the program exits. Return Value - On success, sd_bus_flush() returns 0 or a positive integer. On failure, it returns a - negative errno-style error code. + On success, sd_bus_flush() returns a non-negative integer. On + failure, it returns a negative errno-style error code. Errors @@ -76,7 +96,8 @@ -ECHILD - The bus connection has been created in a different process. + The bus connection has been created in a different process. + diff --git a/man/sd_bus_creds_get_pid.xml b/man/sd_bus_creds_get_pid.xml index a7690d581..a3e8079c5 100644 --- a/man/sd_bus_creds_get_pid.xml +++ b/man/sd_bus_creds_get_pid.xml @@ -332,7 +332,7 @@ information, in particular it should not be used for security-relevant decisions. That's because the executable might have been replaced or removed by the time the value can be processed. Moreover, the kernel exports this information in an ambiguous way (i.e. a deleted executable cannot be safely - distinguished from one whose name suffix is (deleted). + distinguished from one whose name suffix is (deleted)). sd_bus_creds_get_cmdline() will retrieve an array of command line arguments (as stored in diff --git a/man/sd_bus_default.xml b/man/sd_bus_default.xml index 51c27f04f..8532c2bf4 100644 --- a/man/sd_bus_default.xml +++ b/man/sd_bus_default.xml @@ -186,14 +186,14 @@ Note that entering a container is a privileged operation, and will likely only work for the root user on the remote machine. - sd_bus_open_system_machine() connects - to the system bus in the specified machine, - where machine is the name of a local - container. See - machinectl1 - for more information about the "machine" concept. Note that - connections into local containers are only available to privileged - processes at this time. + sd_bus_open_system_machine() connects to the system bus in the specified + machine, where machine is the name of a local + container. See + sd_bus_set_address3 + for a description of the address syntax, and + machinectl1 for more + information about the "machine" concept. Note that connections into local containers are only available + to privileged processes at this time. These calls allocate a bus connection object and initiate the connection to a well-known bus of some form. An alternative to @@ -297,7 +297,7 @@ - In addition, any further connection-related errors may be by returned. See + In addition, other connection-related errors may be returned. See sd_bus_send3. @@ -313,6 +313,7 @@ sd_bus_new3, sd_bus_ref3, sd_bus_unref3, + sd_bus_close3, ssh1, systemd-machined.service8, machinectl1 diff --git a/man/sd_bus_emit_signal.xml b/man/sd_bus_emit_signal.xml new file mode 100644 index 000000000..26ec7d1d7 --- /dev/null +++ b/man/sd_bus_emit_signal.xml @@ -0,0 +1,243 @@ + + + + + + + + sd_bus_emit_signal + systemd + + + + sd_bus_emit_signal + 3 + + + + sd_bus_emit_signal + sd_bus_emit_signalv + sd_bus_emit_interfaces_added + sd_bus_emit_interfaces_added_strv + sd_bus_emit_interfaces_removed + sd_bus_emit_interfaces_removed_strv + sd_bus_emit_properties_changed + sd_bus_emit_properties_changed_strv + sd_bus_emit_object_added + sd_bus_emit_object_removed + + Convenience functions for emitting (standard) D-Bus signals + + + + + #include <systemd/sd-bus-vtable.h> + + + int sd_bus_emit_signal + sd_bus *bus + const char *path + const char *interface + const char *member + const char *types + ... + + + + int sd_bus_emit_signalv + sd_bus *bus + const char *path + const char *interface + const char *member + const char *types + va_list ap + + + + int sd_bus_emit_interfaces_added + sd_bus *bus + const char *path + const char *interface + ... + + + + int sd_bus_emit_interfaces_added_strv + sd_bus *bus + const char *path + const char **interfaces + + + + int sd_bus_emit_interfaces_removed + sd_bus *bus + const char *path + const char *interface + ... + + + + int sd_bus_emit_interfaces_removed_strv + sd_bus *bus + const char *path + const char **interfaces + + + + int sd_bus_emit_properties_changed + sd_bus *bus + const char *path + const char *interface + const char *name + ... + + + + int sd_bus_emit_properties_changed_strv + sd_bus *bus + const char *path + const char *interface + const char **names + + + + int sd_bus_emit_object_added + sd_bus *bus + const char *path + + + + int sd_bus_emit_object_removed + sd_bus *bus + const char *path + + + + + + Description + + sd_bus_emit_signal() is a convenience function for initializing a + bus message object and emitting the corresponding D-Bus signal. It combines the + sd_bus_message_new_signal3, + sd_bus_message_append3 and + sd_bus_send3 + functions into a single function call. sd_bus_emit_signalv() is + equivalent to sd_bus_message_append(), except that it is called with a + va_list instead of a variable number of arguments. + + sd_bus_emit_interfaces_added() and + sd_bus_emit_interfaces_removed() are used to implement the + InterfacesAdded and InterfacesRemoved signals of the + org.freedesktop.DBus.ObjectManager interface. They take a path whose + interfaces have been modified as an argument and a variable list of interfaces that have been + added or removed, respectively. The final argument passed to + sd_bus_emit_interfaces_added() and + sd_bus_emit_interfaces_removed() must be + NULL. This allows both functions to safely determine the number of passed + interface arguments. sd_bus_emit_interfaces_added_strv() and + sd_bus_emit_interfaces_removed_strv() are identical to their respective + counterparts but both take the list of interfaces as a single argument instead of a variable + number of arguments. + + sd_bus_emit_properties_changed() is used to implement the + PropertiesChanged signal of the + org.freedesktop.DBus.Properties interface. It takes an object path, + interface and a variable list of property names as its arguments. The final argument passed to + sd_bus_emit_properties_changed() must be + NULL. This allows it to safely determine the number of passed property + names. sd_bus_emit_properties_changed_strv() is identical to + sd_bus_emit_properties_changed() but takes the list of property names as a + single argument instead of a variable number of arguments. + + sd_bus_emit_object_added() and + sd_bus_emit_object_removed() are convenience functions for emitting the + InterfacesAdded or InterfacesRemoved signals for all + interfaces registered on a specific object path, respectively. This includes any parent fallback + vtables if they are not overridden by a more applicable child vtable. It also includes all the + standard D-Bus interfaces implemented by sd-bus itself on any registered object. + + Note that sd_bus_emit_interfaces_added(), + sd_bus_emit_interfaces_removed(), + sd_bus_emit_object_added() and + sd_bus_emit_object_removed() require an object manager to have been + registered on the given object path or one of its parent object paths using + sd_bus_add_object_manager3. + + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + One of the required parameters is NULL or invalid. A + reserved D-Bus interface was passed as the interface parameter. + + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + -ESRCH + + One of sd_bus_emit_interfaces_added(), + sd_bus_emit_interfaces_removed(), + sd_bus_emit_object_added() or + sd_bus_emit_object_removed() was called on an object without an + object manager registered on its own object path or one of its parent object paths. + + + + + See the man pages of + sd_bus_message_new_signal3, + sd_bus_message_append3 and + sd_bus_send3 + for more possible errors. + + + + + + + See Also + + + sd-bus3, + busctl1, + sd_bus_message_new_signal3, + sd_bus_message_append3, + sd_bus_send3, + sd_bus_call_method3 + + + diff --git a/man/sd_bus_enqueue_for_read.xml b/man/sd_bus_enqueue_for_read.xml index 3318a3031..f948b5914 100644 --- a/man/sd_bus_enqueue_for_read.xml +++ b/man/sd_bus_enqueue_for_read.xml @@ -19,7 +19,7 @@ sd_bus_enqueue_for_read - Re-enqueue a bus message on a bus connection, for reading. + Re-enqueue a bus message on a bus connection, for reading diff --git a/man/sd_bus_get_current_handler.xml b/man/sd_bus_get_current_handler.xml new file mode 100644 index 000000000..0a1843a84 --- /dev/null +++ b/man/sd_bus_get_current_handler.xml @@ -0,0 +1,86 @@ + + + + + + + + sd_bus_get_current_handler + systemd + + + + sd_bus_get_current_handler + 3 + + + + sd_bus_get_current_handler + sd_bus_get_current_message + sd_bus_get_current_slot + sd_bus_get_current_userdata + + Query information of the callback a bus object is currently running + + + + + #include <systemd/sd-bus.h> + + + + + sd_bus_message_handler_t sd_bus_get_current_handler + sd_bus *bus + + + + sd_bus_message* sd_bus_get_current_message + sd_bus *bus + + + + sd_bus_slot* sd_bus_get_current_slot + sd_bus *bus + + + + void* sd_bus_get_current_userdata + sd_bus *bus + + + + + + Description + + Whenever sd-bus is about to invoke a user-supplied callback function, it stores the + current callback, D-Bus message, slot and userdata pointer and allows these to be queried via + sd_bus_get_current_handler(), + sd_bus_get_current_message(), + sd_bus_get_current_slot() and + sd_bus_get_current_userdata(), respectively. If bus + cannot be resolved or if execution does not reside in a user-supplied callback of + bus, these functions return NULL. + + + + Return Value + + On success, these functions return the requested object. On failure, they return + NULL. + + + + + + See Also + + + systemd1, + sd-bus3 + + + + diff --git a/man/sd_bus_get_fd.xml b/man/sd_bus_get_fd.xml index 6a022b1a1..466606118 100644 --- a/man/sd_bus_get_fd.xml +++ b/man/sd_bus_get_fd.xml @@ -22,10 +22,12 @@ sd_bus_get_fd + sd_bus_set_fd sd_bus_get_events sd_bus_get_timeout - Get the file descriptor, I/O events and time-out to wait for from a message bus object + Get the file descriptor, I/O events and timeout to wait for from a message bus + object @@ -37,6 +39,13 @@ sd_bus *bus + + int sd_bus_set_fd + sd_bus *bus + int input_fd + int output_fd + + int sd_bus_get_events sd_bus *bus @@ -53,57 +62,74 @@ Description - sd_bus_get_fd() returns the file descriptor used to communicate from a message bus - object. This descriptor can be used with poll3 or a similar - function to wait for I/O events on the specified bus connection object. If the bus object was configured with the - sd_bus_set_fd3 function, then - the input_fd file descriptor used in that call is returned. + sd_bus_get_fd() returns the file descriptor used to communicate from + a message bus object. This descriptor can be used with + poll3 + or a similar function to wait for I/O events on the specified bus connection object. If the bus + object was configured with the sd_bus_set_fd() function, then the + input_fd file descriptor used in that call is returned. - sd_bus_get_events() returns the I/O events to wait for, suitable for passing to - poll() or a similar call. Returns a combination of POLLIN, - POLLOUT, … events, or negative on error. + sd_bus_set_fd() sets the file descriptors used to communicate from a + message bus object. Both input_fd and output_fd + must be valid file descriptors. The same file descriptor may be used as both the input and the + output file descriptor. This function must be called before the bus is started. - sd_bus_get_timeout() returns the time-out in µs to pass to to - poll() or a similar call when waiting for events on the specified bus connection. The returned - time-out may be zero, in which case a subsequent I/O polling call should be invoked in non-blocking mode. The - returned timeout may be UINT64_MAX in which case the I/O polling call may block indefinitely, - without any applied time-out. Note that the returned time-out should be considered only a maximum sleeping time. It - is permissible (and even expected) that shorter time-outs are used by the calling program, in case other event - sources are polled in the same event loop. Note that the returned time-value is relative and specified in - microseconds. When converting this value in order to pass it as third argument to poll() - (which expects milliseconds), care should be taken to use a division that rounds up to ensure the I/O polling - operation doesn't sleep for shorter than necessary, which might result in unintended busy looping (alternatively, - use ppoll3 - instead of plain poll(), which understands time-outs with nano-second granularity). + sd_bus_get_events() returns the I/O events to wait for, suitable for + passing to poll() or a similar call. Returns a combination of + POLLIN, POLLOUT, … events, or negative on error. + - These three functions are useful to hook up a bus connection object with an external or manual event loop - involving poll() or a similar I/O polling call. Before each invocation of the I/O polling - call, all three functions should be invoked: the file descriptor returned by sd_bus_get_fd() - should be polled for the events indicated by sd_bus_get_events(), and the I/O call should - block for that up to the time-out returned by sd_bus_get_timeout(). After each I/O polling + sd_bus_get_timeout() returns the timeout in µs to pass to + poll() or a similar call when waiting for events on the specified bus + connection. The returned timeout may be zero, in which case a subsequent I/O polling call + should be invoked in non-blocking mode. The returned timeout may be + UINT64_MAX in which case the I/O polling call may block indefinitely, + without any applied timeout. Note that the returned timeout should be considered only a + maximum sleeping time. It is permissible (and even expected) that shorter timeouts are used by + the calling program, in case other event sources are polled in the same event loop. Note that + the returned time-value is relative and specified in microseconds. When converting this value in + order to pass it as third argument to poll() (which expects milliseconds), + care should be taken to use a division that rounds up to ensure the I/O polling operation + doesn't sleep for shorter than necessary, which might result in unintended busy looping + (alternatively, use + ppoll3 + instead of plain poll(), which understands timeouts with nano-second + granularity). + + These three functions are useful to hook up a bus connection object with an external or + manual event loop involving poll() or a similar I/O polling call. Before + each invocation of the I/O polling call, all three functions should be invoked: the file + descriptor returned by sd_bus_get_fd() should be polled for the events + indicated by sd_bus_get_events(), and the I/O call should block for that up + to the timeout returned by sd_bus_get_timeout(). After each I/O polling call the bus connection needs to process incoming or outgoing data, by invoking - sd_bus_process3. + sd_bus_process3. + - Note that these function are only one of three supported ways to implement I/O event handling for bus - connections. Alternatively use - sd_bus_attach_event3 to attach a - bus connection to an sd-event3 - event loop. Or use sd_bus_wait3 + Note that these functions are only one of three supported ways to implement I/O event + handling for bus connections. Alternatively use + sd_bus_attach_event3 + to attach a bus connection to an + sd-event3 + event loop. Or use + sd_bus_wait3 as a simple synchronous, blocking I/O waiting call. Return Value - sd_bus_get_fd() returns the file descriptor used for communication, or a negative - errno-style error code on error. + On success, sd_bus_get_fd() returns the file descriptor used for + communication. On failure, it returns a negative errno-style error code. - sd_bus_get_events() returns the I/O event mask to use for I/O event watching, or a - negative errno-style error code on error. + On success, sd_bus_set_fd() returns a non-negative integer. On + failure, it returns a negative errno-style error code. - sd_bus_get_timeout() returns zero or positive on success, or a negative - errno-style error code on error. + On success, sd_bus_get_events() returns the I/O event mask to use for + I/O event watching. On failure, it returns a negative errno-style error code. + + On success, sd_bus_get_timeout() returns a non-negative integer. On + failure, it returns a negative errno-style error code. Errors @@ -120,8 +146,8 @@ -ECHILD - The bus connection was allocated in a parent process and is being reused in a child - process after fork(). + The bus connection was allocated in a parent process and is being reused + in a child process after fork(). @@ -137,6 +163,19 @@ sd_bus_set_fd(), which sd_bus_get_fd() cannot return. + + + -EBADF + + An invalid file descriptor was passed to + sd_bus_set_fd(). + + + + -ENOPKG + + The bus cannot be resolved. + @@ -149,7 +188,6 @@ systemd1, sd-bus3, - sd_bus_set_fd3, sd_bus_process3, sd_bus_attach_event3, sd_bus_wait3, diff --git a/man/sd_bus_get_name_creds.xml b/man/sd_bus_get_name_creds.xml new file mode 100644 index 000000000..373133657 --- /dev/null +++ b/man/sd_bus_get_name_creds.xml @@ -0,0 +1,121 @@ + + + + + + + + sd_bus_get_name_creds + systemd + + + + sd_bus_get_name_creds + 3 + + + + sd_bus_get_name_creds + sd_bus_get_owner_creds + + Query bus client credentials + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_get_name_creds + sd_bus *bus + const char *name + uint64_t mask + sd_bus_creds **creds + + + + int sd_bus_get_owner_creds + sd_bus *bus + uint64_t mask + sd_bus_creds **creds + + + + + + Description + + sd_bus_get_name_creds() queries the credentials of the bus client + identified by name. The mask parameter is a combo of + SD_BUS_CREDS_* flags that indicate which credential info the caller is + interested in. See + sd_bus_creds_new_from_pid3 + for a list of possible flags. On success, creds contains a new + sd_bus_creds instance with the requested information. Ownership of this instance + belongs to the caller and it should be freed once no longer needed by calling + sd_bus_creds_unref3. + + + sd_bus_get_owner_creds() queries the credentials of the creator of the given + bus. The mask and creds parameters behave the same as in + sd_bus_get_name_creds(). + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + An argument is invalid. + + + + -ENOPKG + + The bus cannot be resolved. + + + + -EPERM + + The bus has already been started. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_creds_unref3 + + + diff --git a/man/sd_bus_get_name_machine_id.xml b/man/sd_bus_get_name_machine_id.xml new file mode 100644 index 000000000..8f3ce6436 --- /dev/null +++ b/man/sd_bus_get_name_machine_id.xml @@ -0,0 +1,98 @@ + + + + + + + + sd_bus_get_name_machine_id + systemd + + + + sd_bus_get_name_machine_id + 3 + + + + sd_bus_get_name_machine_id + + Retrieve a bus client's machine identity + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_get_name_machine_id + sd_bus *bus + const char *name + sd_id128_t *machine + + + + + + Description + + sd_bus_get_name_machine_id() retrieves the D-Bus machine identity of the + machine that the bus client identified by name is running on. Internally, it calls + the GetMachineId method of the org.freedesktop.DBus.Peer + interface. The D-Bus machine identity is a 128-bit UUID. On Linux systems running systemd, this + corresponds to the contents of /etc/machine-id. On success, the machine identity is + stored in machine. + + + + Return Value + + On success, this function returns a non-negative integer. On failure, it returns a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + An argument is invalid. + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + systemd1, + sd-bus3 + + + + diff --git a/man/sd_bus_interface_name_is_valid.xml b/man/sd_bus_interface_name_is_valid.xml new file mode 100644 index 000000000..a72024e5a --- /dev/null +++ b/man/sd_bus_interface_name_is_valid.xml @@ -0,0 +1,98 @@ + + + + + + + sd_bus_interface_name_is_valid + systemd + + + + sd_bus_interface_name_is_valid + 3 + + + + sd_bus_interface_name_is_valid + sd_bus_service_name_is_valid + sd_bus_member_name_is_valid + sd_bus_object_path_is_valid + + Check if a string is a valid bus name or object path + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_interface_name_is_valid + const char* p + + + + int sd_bus_service_name_is_valid + const char* p + + + + int sd_bus_member_name_is_valid + const char* p + + + + int sd_bus_object_path_is_valid + const char* p + + + + + + Description + + sd_bus_interface_name_is_valid() checks if a given string + p is a syntactically valid bus interface name. Similarly, + sd_bus_service_name_is_valid() checks if the argument is a valid bus service name, + sd_bus_member_name_is_valid() checks if the argument is a valid bus interface member + name, and sd_bus_object_path_is_valid() checks if the argument is a valid bus object + path. Those functions generally check that only allowed characters are used and that the length of the + string is within limits. + + + + Return Value + + Those functions return 1 if the argument is a valid interface / service / member name or object + path, and 0 if it is not. If the argument is NULL, an error is returned. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The p parameter is + NULL. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call_method3 + + + + diff --git a/man/sd_bus_is_open.xml b/man/sd_bus_is_open.xml index d993142cb..4a21189c1 100644 --- a/man/sd_bus_is_open.xml +++ b/man/sd_bus_is_open.xml @@ -20,7 +20,7 @@ sd_bus_is_open sd_bus_is_ready - Check whether the a bus connection is open or ready. + Check whether the bus connection is open or ready diff --git a/man/sd_bus_list_names.xml b/man/sd_bus_list_names.xml new file mode 100644 index 000000000..ad7ecd00e --- /dev/null +++ b/man/sd_bus_list_names.xml @@ -0,0 +1,110 @@ + + + + + + + + sd_bus_list_names + systemd + + + + sd_bus_list_names + 3 + + + + sd_bus_list_names + + Retrieve information about registered names on a bus + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_list_names + sd_bus *bus + char ***acquired + char ***activatable + + + + + + Description + + sd_bus_list_names() retrieves information about the registered names on a bus. + If acquired is not NULL, this function calls + + org.freedesktop.DBus.ListNames to retrieve the list of currently-owned names on the bus. If + acquired is not NULL, the function calls + + org.freedesktop.DBus.ListActivableNames to retrieve the list of all names on the bus that can be + activated. Note that ownership of the arrays returned by sd_bus_list_names() in + acquired and activatable is transferred to the caller and + hence, the caller is responsible for freeing these arrays and their contents. + + + + Return Value + + On success, sd_bus_list_names() returns a non-negative integer. On failure, + it returns a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + bus or both acquired and + activatable were NULL. + + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + + + + -ENOMEM + + Memory allocation failed. + + + + -ENOTCONN + + The bus is not connected. + + + + + + + + + See Also + + + systemd1, + sd-bus3 + + + diff --git a/man/sd_bus_message_append.xml b/man/sd_bus_message_append.xml index b87468e37..5faadd603 100644 --- a/man/sd_bus_message_append.xml +++ b/man/sd_bus_message_append.xml @@ -20,8 +20,7 @@ sd_bus_message_append sd_bus_message_appendv - Attach fields to a D-Bus message based on a type - string + Attach fields to a D-Bus message based on a type string @@ -36,10 +35,10 @@ - int sd_bus_message_appendv - sd_bus_message *m - const char *types - va_list ap + int sd_bus_message_appendv + sd_bus_message *m + const char *types + va_list ap @@ -48,61 +47,50 @@ Description - The sd_bus_message_append() function - appends a sequence of fields to the D-Bus message object - m. The type string - types describes the types of the field - arguments that follow. For each type specified in the type string, - one or more arguments need to be specified, in the same order as - declared in the type string. + The sd_bus_message_append() function appends a sequence of fields to + the D-Bus message object m. The type string types + describes the types of the field arguments that follow. For each type specified in the type + string, one or more arguments need to be specified, in the same order as declared in the type + string. - The type string is composed of the elements shown in the - table below. It contains zero or more single "complete types". - Each complete type may be one of the basic types or a fully - described container type. A container type may be a structure with - the contained types, a variant, an array with its element type, or - a dictionary entry with the contained types. The type string is - NUL-terminated. + The type string is composed of the elements shown in the table below. It contains zero or + more single "complete types". Each complete type may be one of the basic types or a fully + described container type. A container type may be a structure with the contained types, a + variant, an array with its element type, or a dictionary entry with the contained types. The + type string is NUL-terminated. - In case of a basic type, one argument of the corresponding - type is expected. + In case of a basic type, one argument of the corresponding type is expected. - A structure is denoted by a sequence of complete types - between ( and ). This - sequence cannot be empty — it must contain at least one type. - Arguments corresponding to this nested sequence follow the same - rules as if they were not nested. + A structure is denoted by a sequence of complete types between ( and + ). This sequence cannot be empty — it must contain at least one type. + Arguments corresponding to this nested sequence follow the same rules as if they were not + nested. - A variant is denoted by v. Corresponding - arguments must begin with a type string denoting a complete type, - and following that, arguments corresponding to the specified type. + A variant is denoted by v. Corresponding arguments must begin with a + type string denoting a complete type, and following that, arguments corresponding to the + specified type. + + An array is denoted by a followed by a complete type. Corresponding + arguments must begin with the number of entries in the array, followed by the entries + themselves, matching the element type of the array. + + A dictionary is an array of dictionary entries, denoted by a followed + by a pair of complete types between { and }. The first of + those types must be a basic type. Corresponding arguments must begin with the number of + dictionary entries, followed by a pair of values for each entry matching the element type of the + dictionary entries. + + sd_bus_message_appendv() is equivalent to + sd_bus_message_append(), except that it is called with a + va_list instead of a variable number of arguments. This function does not + call the va_end() macro. Because it invokes the + va_arg() macro, the value of ap is undefined after + the call. + + For further details on the D-Bus type system, please consult the + D-Bus Specification. - An array is denoted by a followed by a - complete type. Corresponding arguments must begin with the number of - entries in the array, followed by the entries themselves, - matching the element type of the array. - - A dictionary is an array of dictionary entries, denoted by - a followed by a pair of complete types between - { and }. The first of those - types must be a basic type. Corresponding arguments must begin - with the number of dictionary entries, followed by a pair of - values for each entry matching the element type of - the dictionary entries. - - The sd_bus_message_appendv() is equivalent to the - sd_bus_message_append(), except that it is called with - a va_list instead of a variable number of arguments. This - function does not call the va_end() macro. Because it - invokes the va_arg() macro, the value of - ap is undefined after the call. - - For further details on the D-Bus type system, please consult - the D-Bus - Specification. - Item type specifiers @@ -162,7 +150,6 @@ NULL, which is equivalent to an empty string. See sd_bus_message_append_basic3 for the precise interpretation of those and other types. - @@ -205,12 +192,12 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d); Append a structure composed of a string and a D-Bus path: sd_bus_message_append(m, "(so)", "a string", "/a/path"); - + Append an array of UNIX file descriptors: sd_bus_message_append(m, "ah", 3, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO); - + Append a variant, with the real type "g" (signature), and value "sdbusisgood": @@ -227,8 +214,8 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d); Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative - errno-style error code. + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. @@ -242,7 +229,8 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d); systemd1, sd-bus3, sd_bus_message_append_basic3, - sd_bus_message_append_array3 + sd_bus_message_append_array3, + sd_bus_message_open_container3 diff --git a/man/sd_bus_message_at_end.xml b/man/sd_bus_message_at_end.xml new file mode 100644 index 000000000..ce21c7e74 --- /dev/null +++ b/man/sd_bus_message_at_end.xml @@ -0,0 +1,86 @@ + + + + + + + sd_bus_message_at_end + systemd + + + + sd_bus_message_at_end + 3 + + + + sd_bus_message_at_end + + Check if a message has been fully read + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_at_end + sd_bus_message *m + int complete + + + + + + Description + + sd_bus_message_at_end() returns whether all data from the currently opened + container in m or all data from all containers in m has + been read. If complete is zero, this function returns whether all data from the + currently opened container has been read. If complete is non-zero, this function + returns whether all data from all containers in m has been read. + + + + Return Value + + If all data from all containers or the current container (depending on the value of + complete) has been read, sd_bus_message_at_end() returns a + positive integer. If there is still data left to be read, it returns zero. On failure, it returns a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The m parameter is NULL. + + + + + -EPERM + + The message is not sealed. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_read3 + + + diff --git a/man/sd_bus_message_dump.xml b/man/sd_bus_message_dump.xml index db9e46d99..720b11427 100644 --- a/man/sd_bus_message_dump.xml +++ b/man/sd_bus_message_dump.xml @@ -65,7 +65,7 @@ Output for a signal message (with SD_BUS_MESSAGE_DUMP_WITH_HEADER): -‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=22 +‣ Type=signal Endian=l Flags=1 Version=1 Cookie=22 Path=/value/a Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged MESSAGE "sa{sv}as" { STRING "org.freedesktop.systemd.ValueTest"; diff --git a/man/sd_bus_message_get_type.xml b/man/sd_bus_message_get_type.xml index d8a45ce7a..442c763a4 100644 --- a/man/sd_bus_message_get_type.xml +++ b/man/sd_bus_message_get_type.xml @@ -17,11 +17,14 @@ sd_bus_message_get_type + sd_bus_message_get_error + sd_bus_message_get_errno + sd_bus_message_get_creds sd_bus_message_is_signal sd_bus_message_is_method_call sd_bus_message_is_method_error - Query bus message addressing metadata + Query bus message addressing/credentials metadata @@ -30,27 +33,42 @@ int sd_bus_message_get_type - sd_bus_message *message + sd_bus_message *m uint8_t *type + + sd_bus_error* sd_bus_message_get_error + sd_bus_message *m + + + + int sd_bus_message_get_errno + sd_bus_message *m + + + + sd_bus_creds* sd_bus_message_get_creds + sd_bus_message *m + + int sd_bus_message_is_signal - sd_bus_message *message + sd_bus_message *m const char *interface const char *member int sd_bus_message_is_method_call - sd_bus_message *message + sd_bus_message *m const char *interface const char *member int sd_bus_message_is_method_error - sd_bus_message *message + sd_bus_message *m const char *name @@ -62,40 +80,58 @@ sd_bus_message_get_type() returns the type of a message in the output parameter type, one of SD_BUS_MESSAGE_METHOD_CALL, - SD_BUS_MESSAGE_METHOD_RETURN, - SD_BUS_MESSAGE_METHOD_ERROR, SD_BUS_MESSAGE_SIGNAL. - This type is either specified as a parameter when the message is created using - sd_bus_set_message_new3, + SD_BUS_MESSAGE_METHOD_RETURN, SD_BUS_MESSAGE_METHOD_ERROR, + SD_BUS_MESSAGE_SIGNAL. This type is either specified as a parameter when the message + is created using + sd_bus_message_new3, or is set automatically when the message is created using - sd_bus_set_message_new_signal3, - sd_bus_set_message_new_method_call3, - sd_bus_set_message_new_method_error3 - and similar functions. + sd_bus_message_new_signal3, + sd_bus_message_new_method_call3, + sd_bus_message_new_method_error3 + and similar functions. + + sd_bus_message_get_error() returns the error stored in the message + m, if there is any. Otherwise, it returns NULL. + sd_bus_message_get_errno() returns the error stored in the message + m as a positive errno-style value, if there is any. Otherwise, it returns zero. + Errors are mapped to errno values according to the default and any additional registered error mappings. + See sd-bus-errors3 and + sd_bus_error_add_map3. - sd_bus_message_is_signal() checks if message m - is a signal message. If interface is non-null, it also checks if the - message has the same interface set. If member is non-null, it also checks - if the message has the same member set. Also see - sd_bus_set_message_new_signal3. It returns true when all checks pass. + sd_bus_message_get_creds() returns the message credentials attached to the + message m. If no credentials are attached to the message, it returns + NULL. Ownership of the credentials instance is not transferred to the caller and + hence should not be freed. + + sd_bus_message_is_signal() checks if message m is a + signal message. If interface is non-null, it also checks if the message has the + same interface set. If member is non-null, it also checks if the message has the + same member set. Also see + sd_bus_message_new_signal3. + It returns true when all checks pass. sd_bus_message_is_method_call() checks if message m - is a method call message. If interface is non-null, it also checks if the - message has the same interface set. If member is non-null, it also checks - if the message has the same member set. Also see - sd_bus_set_message_new_method_call3. It returns true when all checks pass. + is a method call message. If interface is non-null, it also checks if the message + has the same interface set. If member is non-null, it also checks if the message + has the same member set. Also see + sd_bus_message_new_method_call3. + It returns true when all checks pass.sd_bus_message_is_method_error() checks if message m - is an error reply message. If name is non-null, it also checks if the - message has the same error identifier set. Also see - sd_bus_set_message_new_method_error3. It returns true when all checks pass. - + is an error reply message. If name is non-null, it also checks if the message has + the same error identifier set. Also see + sd_bus_message_new_method_error3. + It returns true when all checks pass. + Return Value - On success, those functions return 0 or a positive - integer. On failure, it returns a negative errno-style error code. + On success, these functions (except sd_bus_message_get_error() and + sd_bus_message_get_creds()) return a non-negative integer. On failure, they return a + negative errno-style error code. sd_bus_message_get_errno() always returns a + non-negative integer, even on failure. Errors @@ -106,7 +142,7 @@ -EINVAL - The message parameter or the output parameter are + The message parameter m or an output parameter is NULL. @@ -122,7 +158,9 @@ systemd1, sd-bus3, sd_bus_message_new3, - sd_bus_message_set_destination3 + sd_bus_message_set_destination3, + sd-bus-errors3, + sd_bus_error_add_map3 diff --git a/man/sd_bus_message_new_method_call.xml b/man/sd_bus_message_new_method_call.xml index 0d181ed82..cfb13af51 100644 --- a/man/sd_bus_message_new_method_call.xml +++ b/man/sd_bus_message_new_method_call.xml @@ -28,7 +28,7 @@ #include <systemd/sd-bus.h> - int sd_bus_message_new_method_call + int sd_bus_message_new_method_call sd_bus *bus sd_bus_message **m const char *destination @@ -38,7 +38,7 @@ - int sd_bus_message_new_method_return + int sd_bus_message_new_method_return sd_bus_message *call sd_bus_message **m @@ -70,7 +70,11 @@ has only a single member with the given name and there is no ambiguity if the interface name is omitted. - The sd_bus_message_new_method_call() function creates a new bus + Note that this is a low level interface. See + sd_bus_call_method3 + for a more convenient way of calling D-Bus methods. + + The sd_bus_message_new_method_return() function creates a new bus message object that is a reply to the method call call and returns it in the m output parameter. The call parameter must be a method call message. The sender of call is used as the destination. @@ -80,8 +84,8 @@ Return Value - This function returns 0 if the message object was successfully created, and a negative - errno-style error code otherwise. + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. Errors @@ -159,6 +163,8 @@ systemd1, sd-bus3, + sd_bus_call3, + sd_bus_call_method3, sd_bus_path_encode3 diff --git a/man/sd_bus_message_new_method_error.xml b/man/sd_bus_message_new_method_error.xml index 0c471c534..39bb24c3a 100644 --- a/man/sd_bus_message_new_method_error.xml +++ b/man/sd_bus_message_new_method_error.xml @@ -22,7 +22,7 @@ sd_bus_message_new_method_errno sd_bus_message_new_method_errnof - Create a an error reply for a method call + Create an error reply for a method call @@ -98,7 +98,7 @@ an error reply similarly to sd_bus_message_new_method_error(), but in addition to the error structure p, it takes an - errno3 + errno3 error value in parameter error. If the error p is set (see sd_bus_error_is_set3), @@ -111,7 +111,7 @@ The sd_bus_message_new_method_errnof() function creates an error reply similarly to sd_bus_message_new_method_error(). It takes an - errno3 + errno3 error value in parameter error, plus a printf3 format string format and corresponding arguments. diff --git a/man/sd_bus_message_new_signal.xml b/man/sd_bus_message_new_signal.xml index 61619304d..5ac35e7a3 100644 --- a/man/sd_bus_message_new_signal.xml +++ b/man/sd_bus_message_new_signal.xml @@ -45,7 +45,7 @@ parameter. The signal will be sent to path path, on the interface interface, member member. When this message is sent, no reply is expected. See - sd_bus_message_new_call1 + sd_bus_message_new_method_call1 for a short description of the meaning of the path, interface, and member parameters. @@ -113,7 +113,8 @@ systemd1, - sd-bus3 + sd-bus3, + sd_bus_emit_signal3 diff --git a/man/sd_bus_message_open_container.xml b/man/sd_bus_message_open_container.xml new file mode 100644 index 000000000..5a6551871 --- /dev/null +++ b/man/sd_bus_message_open_container.xml @@ -0,0 +1,165 @@ + + + + + + + + sd_bus_message_open_container + systemd + + + + sd_bus_message_open_container + 3 + + + + sd_bus_message_open_container + sd_bus_message_close_container + sd_bus_message_enter_container + sd_bus_message_exit_container + + Create and move between containers in D-Bus messages + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_open_container + sd_bus_message *m + char type + const char *contents + + + + int sd_bus_message_close_container + sd_bus_message *m + + + + int sd_bus_message_enter_container + sd_bus_message *m + char type + const char *contents + + + + int sd_bus_message_exit_container + sd_bus_message *m + + + + + + Description + + sd_bus_message_open_container() appends a new container to the message + m. After opening a new container, it can be filled with content using + sd_bus_message_append3 + and similar functions. Containers behave like a stack. To nest containers inside each other, call + sd_bus_message_open_container() multiple times without calling + sd_bus_message_close_container() in between. Each container will be nested inside the + previous container. type represents the container type and should be one of + r, a, v or e as described in + sd_bus_message_append3. + Instead of literals, the corresponding constants SD_BUS_TYPE_STRUCT, + SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT or + SD_BUS_TYPE_DICT_ENTRY can also be used. contents describes + the type of the container's elements and should be a D-Bus type string following the rules described in + sd_bus_message_append3. + + + sd_bus_message_close_container() closes the last container opened with + sd_bus_message_open_container(). On success, the write pointer of the message + m is positioned after the closed container in its parent container or in + m itself if there is no parent container. + + sd_bus_message_enter_container() enters the next container of the message + m. It behaves mostly the same as + sd_bus_message_open_container(). Entering a container allows reading its contents + with + sd_bus_message_read3 + and similar functions. type and contents are the same as in + sd_bus_message_open_container(). + + sd_bus_message_exit_container() exits the scope of the last container entered + with sd_bus_message_enter_container(). It behaves mostly the same as + sd_bus_message_close_container(). + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + m or contents are + NULL or type is invalid. + + + + -EPERM + + The message m is already sealed. + + + + -ESTALE + + The message m is in an invalid state. + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + Examples + + + Append an array of strings to a message + + + + + + Read an array of strings from a message + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append3, + sd_bus_message_read3, + The D-Bus specification + + + + diff --git a/man/sd_bus_message_read.xml b/man/sd_bus_message_read.xml index 2afa44174..1b9f36cd8 100644 --- a/man/sd_bus_message_read.xml +++ b/man/sd_bus_message_read.xml @@ -19,6 +19,7 @@ sd_bus_message_read sd_bus_message_readv + sd_bus_message_peek_type Read a sequence of values from a message @@ -30,48 +31,52 @@ int sd_bus_message_read sd_bus_message *m - char char *types + const char *types ... int sd_bus_message_readv sd_bus_message *m - char char *types + const char *types va_list ap + + + int sd_bus_message_peek_type + char *type + const char **contents + Description - sd_bus_message_read() reads a sequence of fields from - the D-Bus message object m and advances the read position - in the message. The type string types describes the types - of items expected in the message and the field arguments that follow. The type - string may be NULL or empty, in which case nothing is - read. + sd_bus_message_read() reads a sequence of fields from the D-Bus message object + m and advances the read position in the message. The type string + types describes the types of items expected in the message and the field arguments + that follow. The type string may be NULL or empty, in which case nothing is read. + The type string is composed of the elements described in sd_bus_message_append3, - i.e. basic and container types. It must contain zero or more single "complete - types". The type string is NUL-terminated. + i.e. basic and container types. It must contain zero or more single "complete types". The type string is + NUL-terminated. - For each type specified in the type string, one or more arguments need to be specified - after the types parameter, in the same order. The arguments must be - pointers to appropriate types (a pointer to int8_t for a y in - the type string, a pointer to int32_t for an i, a pointer to - const char* for an s, ...) which are set based on the values in - the message. As an exception, in case or array and variant types, the first argument is an - "input" argument that further specifies how the message should be read. See the table below for - a complete list of allowed arguments and their types. Note that, if the basic type is a pointer - (e.g., const char * in the case of a string), the argument is a pointer to a - pointer, and also the pointer value that is written is only borrowed and the contents must be - copied if they are to be used after the end of the messages lifetime. + For each type specified in the type string, one or more arguments need to be specified after the + types parameter, in the same order. The arguments must be pointers to appropriate + types (a pointer to int8_t for a y in the type string, a pointer to + int32_t for an i, a pointer to const char* for an + s, ...) which are set based on the values in the message. As an exception, in case of + array and variant types, the first argument is an "input" argument that further specifies how the message + should be read. See the table below for a complete list of allowed arguments and their types. Note that, + if the basic type is a pointer (e.g., const char * in the case of a string), the argument is + a pointer to a pointer, and also the pointer value that is written is only borrowed and the contents must + be copied if they are to be used after the end of the messages lifetime. - Each argument may also be NULL, in which case the value is read and - ignored. + Each argument may also be NULL, in which case the value is read and ignored. +
Item type specifiers @@ -139,24 +144,29 @@
- If objects of the specified types are not present at the current position - in the message, an error is returned. - + If objects of the specified types are not present at the current position in the message, an error + is returned. The sd_bus_message_readv() is equivalent to the - sd_bus_message_read(), except that it is called with a - va_list instead of a variable number of arguments. This - function does not call the va_end() macro. Because it - invokes the va_arg() macro, the value of - ap is undefined after the call. + sd_bus_message_read(), except that it is called with a va_list + instead of a variable number of arguments. This function does not call the va_end() + macro. Because it invokes the va_arg() macro, the value of ap + is undefined after the call.
+ + sd_bus_message_peek_type() determines the type of the next element in + m to be read by sd_bus_message_read() or similar functions. + On success, the type is stored in type, if it is not NULL. + If the type is a container type, the type of its elements is stored in contents, + if it is not NULL. If this function successfully determines the type of the next + element in m, it returns a positive integer. If there are no more elements to be + read, it returns zero.
Return Value - On success, sd_bus_message_read() and - sd_bus_message_readv() return 0 or a positive integer. On failure, they return a - negative errno-style error code. + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. @@ -228,7 +238,8 @@ sd_bus_message_read(m, "a{is}", 3, &i, &s, &j, &t, &k, & sd-bus3, sd_bus_message_read_basic3, sd_bus_message_skip3, - sd_bus_message_append3 + sd_bus_message_append3, + sd_bus_message_enter_container3 diff --git a/man/sd_bus_message_read_array.xml b/man/sd_bus_message_read_array.xml index 26e8ebae6..7f9f9703b 100644 --- a/man/sd_bus_message_read_array.xml +++ b/man/sd_bus_message_read_array.xml @@ -105,7 +105,8 @@ systemd1, sd-bus3, - sd_bus_message_read3 + sd_bus_message_read3, + sd_bus_message_read_strv3 diff --git a/man/sd_bus_message_read_strv.xml b/man/sd_bus_message_read_strv.xml new file mode 100644 index 000000000..a86bbe45d --- /dev/null +++ b/man/sd_bus_message_read_strv.xml @@ -0,0 +1,90 @@ + + + + + + + + sd_bus_message_read_strv + systemd + + + + sd_bus_message_read_strv + 3 + + + + sd_bus_message_read_strv + + Access an array of strings in a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_read_strv + sd_bus_message *m + char ***l + + + + + + Description + + sd_bus_message_read_strv() gives access to an array of strings in message + m. The "read pointer" in the message must be right before an array of strings. On + success, a pointer to the NULL-terminated array of strings is returned in the output + parameter l. Note that ownership of this array is transferred to the caller. + Hence, the caller is responsible for freeing this array and its contents. + + + + Return Value + + On success, sd_bus_message_read_strv() returns a non-negative integer. On + failure, it returns a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + m or l are NULL. + + + + + -EPERM + + The message is not sealed. + + + + -EBADMSG + + The message cannot be parsed. + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_read3 + + + + diff --git a/man/sd_bus_message_seal.xml b/man/sd_bus_message_seal.xml new file mode 100644 index 000000000..03783d927 --- /dev/null +++ b/man/sd_bus_message_seal.xml @@ -0,0 +1,106 @@ + + + + + + + + sd_bus_message_seal + systemd + + + + sd_bus_message_seal + 3 + + + + sd_bus_message_seal + + Prepare a D-Bus message for transmission + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_seal + sd_bus_message *m + uint64_t cookie + uint64_t timeout_usec + + + + + + Description + + sd_bus_message_seal() finishes the message m + and prepares it for transmission using + sd_bus_send3. + cookie specifies the identifier used to match the message reply to its + corresponding request. timeout_usec specifies the maximum time in + microseconds to wait for a reply to arrive. + + Note that in most scenarios, it's not necessary to call this function directly. + sd_bus_call3, + sd_bus_call_async3 and + sd_bus_send3 + will seal any given messages if they have not been sealed yet. + + + + Return Value + + On success, this function returns a non-negative integer. On failure, it returns a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The m parameter is NULL. + + + + + -EBADMSG + + The D-Bus message m has open containers. + + + + + -ENOMSG + + The D-Bus message m is a reply but its type + signature does not match the return type signature of its corresponding member in the + object vtable. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call3, + sd_bus_call_async3, + sd_bus_send3 + + + + diff --git a/man/sd_bus_message_sensitive.xml b/man/sd_bus_message_sensitive.xml index a4a732cfd..8f3e8aeb8 100644 --- a/man/sd_bus_message_sensitive.xml +++ b/man/sd_bus_message_sensitive.xml @@ -50,7 +50,7 @@ Return Value - On success, theis functions return 0 or a positive integer. On failure, it returns a + On success, this functions return 0 or a positive integer. On failure, it returns a negative errno-style error code. diff --git a/man/sd_bus_message_set_destination.xml b/man/sd_bus_message_set_destination.xml index ca3e466d7..51da5ff3b 100644 --- a/man/sd_bus_message_set_destination.xml +++ b/man/sd_bus_message_set_destination.xml @@ -82,12 +82,10 @@ member fields from message header. The return value will be NULL is message is NULL or the message is of a type that doesn't use those fields or the message doesn't have them set. See - sd_bus_message_new_method_call3 - and + sd_bus_message_new_method_call3 and sd_bus_message_set_destination3 for more discussion of those values. - sd_bus_message_set_sender() sets the sender service name for the specified bus message object. The specified name must be a valid unique or well-known service name. This function is useful only for messages to send on direct connections as for connections to bus brokers the broker will fill in the destination @@ -123,9 +121,9 @@ -EPERM - For sd_bus_message_set_destination or - sd_bus_message_set_sender, the message is already - sealed. + For sd_bus_message_set_destination() and + sd_bus_message_set_sender(), the message is already sealed. + diff --git a/man/sd_bus_message_set_expect_reply.xml b/man/sd_bus_message_set_expect_reply.xml index 6f22e8205..43a94c83c 100644 --- a/man/sd_bus_message_set_expect_reply.xml +++ b/man/sd_bus_message_set_expect_reply.xml @@ -20,6 +20,8 @@ sd_bus_message_get_expect_reply sd_bus_message_set_auto_start sd_bus_message_get_auto_start + sd_bus_message_set_allow_interactive_authorization + sd_bus_message_get_allow_interactive_authorization Set and query bus message metadata @@ -49,41 +51,61 @@ int sd_bus_message_get_auto_start sd_bus_message *message - + + int sd_bus_message_set_allow_interactive_authorization + sd_bus_message *message + int b + + + + int sd_bus_message_get_allow_interactive_authorization + sd_bus_message *message + +
Description sd_bus_message_set_expect_reply() sets or clears the - NO_REPLY_EXPECTED flag on the message m. This flag - matters only for method call messages and is used to specify that no method return or error - reply is expected. It is ignored for other types. Thus, for a method call message, calling - sd_bus_message_set_expect_reply(…, 0) sets the flag and - suppresses the reply. + NO_REPLY_EXPECTED flag on the message m. This flag matters + only for method call messages and is used to specify that no method return or error reply is expected. + It is ignored for other types. Thus, for a method call message, calling + sd_bus_message_set_expect_reply(…, 0) sets the flag and suppresses the + reply. sd_bus_message_get_expect_reply() checks if the - NO_REPLY_EXPECTED flag is set on the message m. It - will return positive if it is not set, and zero if it is. + NO_REPLY_EXPECTED flag is set on the message m. It will + return positive if it is not set, and zero if it is. sd_bus_message_set_auto_start() sets or clears the - NO_AUTO_START flag on the message m. When the flag - is set the bus must not launch an owner for the destination name in response to this message. - Calling - sd_bus_message_set_auto_start(…, 0) sets the flag. - + NO_AUTO_START flag on the message m. When the flag is set, + the bus must not launch an owner for the destination name in response to this message. Calling + sd_bus_message_set_auto_start(…, 0) sets the flag. sd_bus_message_get_auto_start() checks if the - NO_AUTO_START flag is set on the message m. It - will return positive if it is not set, and zero if it is. + NO_AUTO_START flag is set on the message m. It will return + positive if it is not set, and zero if it is. + + sd_bus_message_set_allow_interactive_authorization() sets or clears the + ALLOW_INTERACTIVE_AUTHORIZATION flag on the message m. + Setting this flag informs the receiver that the caller is prepared to wait for interactive authorization + via polkit or a similar framework. Note that setting this flag does not guarantee that the receiver will + actually perform interactive authorization. Also, make sure to set a suitable message timeout when using + this flag since interactive authorization could potentially take a long time as it depends on user input. + If b is non-zero, the flag is set. + + sd_bus_message_get_allow_interactive_authorization() checks if the + ALLOW_INTERACTIVE_AUTHORIZATION flag is set on the message m. + It will return a positive integer if the flag is set. Otherwise, it returns zero. Return Value - On success, these functions return 0 or a positive integer. On failure, they return a - negative errno-style error code. + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. Errors @@ -94,18 +116,17 @@ -EINVAL - The message parameter is - NULL. + The message parameter is NULL. + -EPERM - The message message is sealed - when trying to set a flag. + + The message message is sealed when trying to set a flag. - The message message has wrong - type. + The message message has wrong type. @@ -123,5 +144,4 @@ sd_bus_set_description3 - diff --git a/man/sd_bus_negotiate_fds.xml b/man/sd_bus_negotiate_fds.xml index c12b65c98..340049fc9 100644 --- a/man/sd_bus_negotiate_fds.xml +++ b/man/sd_bus_negotiate_fds.xml @@ -19,6 +19,7 @@ sd_bus_negotiate_fds sd_bus_negotiate_timestamp sd_bus_negotiate_creds + sd_bus_get_creds_mask Control feature negotiation on bus connections @@ -45,69 +46,69 @@ int b uint64_t mask + + + int sd_bus_get_creds_mask + sd_bus *bus + uint64_t *mask +
Description - sd_bus_negotiate_fds() controls whether - file descriptor passing shall be negotiated for the specified bus - connection. It takes a bus object and a boolean, which, when true, - enables file descriptor passing, and, when false, disables - it. Note that not all transports and servers support file - descriptor passing. In particular, networked transports generally - do not support file descriptor passing. To find out whether file - descriptor passing is available after negotiation, use + sd_bus_negotiate_fds() controls whether file descriptor passing shall be + negotiated for the specified bus connection. It takes a bus object and a boolean, which, when true, + enables file descriptor passing, and, when false, disables it. Note that not all transports and servers + support file descriptor passing. In particular, networked transports generally do not support file + descriptor passing. To find out whether file descriptor passing is available after negotiation, use sd_bus_can_send3 - and pass SD_BUS_TYPE_UNIX_FD. Note that file - descriptor passing is always enabled for both sending and - receiving or for neither, but never only in one direction. By - default, file descriptor passing is negotiated for all - connections. + and pass SD_BUS_TYPE_UNIX_FD. Note that file descriptor passing is always enabled + for both sending and receiving or for neither, but never only in one direction. By default, file + descriptor passing is negotiated for all connections. - sd_bus_negotiate_timestamp() controls whether implicit sender - timestamps shall be attached automatically to all incoming messages. Takes a bus object and a - boolean, which, when true, enables timestamping, and, when false, disables it. Use + sd_bus_negotiate_timestamp() controls whether implicit sender timestamps shall + be attached automatically to all incoming messages. Takes a bus object and a boolean, which, when true, + enables timestamping, and, when false, disables it. Use sd_bus_message_get_monotonic_usec3, sd_bus_message_get_realtime_usec3, sd_bus_message_get_seqnum3 - to query the timestamps of incoming messages. If negotiation is disabled or not supported, these - calls will fail with -ENODATA. Note that currently no transports support - timestamping of messages. By default, message timestamping is not negotiated for - connections. + to query the timestamps of incoming messages. If negotiation is disabled or not supported, these calls + will fail with -ENODATA. Note that currently no transports support timestamping of + messages. By default, message timestamping is not negotiated for connections. sd_bus_negotiate_creds() controls whether and which implicit sender - credentials shall be attached automatically to all incoming messages. Takes a bus object and a - boolean indicating whether to enable or disable the credential parts encoded in the bit mask - value argument. Note that not all transports support attaching sender credentials to messages, - or do not support all types of sender credential parameters, or might suppress them under - certain circumstances for individual messages. Specifically, dbus1 only supports - SD_BUS_CREDS_UNIQUE_NAME. The sender credentials are suitable for - authorization decisions. By default, only SD_BUS_CREDS_WELL_KNOWN_NAMES and - SD_BUS_CREDS_UNIQUE_NAME are enabled. In fact, these two credential fields - are always sent along and cannot be turned off. + credentials shall be attached automatically to all incoming messages. Takes a bus object and a boolean + indicating whether to enable or disable the credential parts encoded in the bit mask value argument. Note + that not all transports support attaching sender credentials to messages, or do not support all types of + sender credential parameters, or might suppress them under certain circumstances for individual messages. + Specifically, dbus1 only supports SD_BUS_CREDS_UNIQUE_NAME. The sender credentials + are suitable for authorization decisions. By default, only + SD_BUS_CREDS_WELL_KNOWN_NAMES and SD_BUS_CREDS_UNIQUE_NAME are + enabled. In fact, these two credential fields are always sent along and cannot be turned off. - The sd_bus_negotiate_fds() function may - be called only before the connection has been started with + sd_bus_get_creds_mask() returns the set of sender credentials that was + negotiated to be attached to all incoming messages in mask. This value is an + upper boundary only. Hence, always make sure to explicitly check which credentials are attached to a + specific message before using it. + + The sd_bus_negotiate_fds() function may be called only before the connection + has been started with sd_bus_start3. Both - sd_bus_negotiate_timestamp() and - sd_bus_negotiate_creds() may also be called - after a connection has been set up. Note that, when operating on a - connection that is shared between multiple components of the same - program (for example via - sd_bus_default3), - it is highly recommended to only enable additional per message - metadata fields, but never disable them again, in order not to - disable functionality needed by other components. + sd_bus_negotiate_timestamp() and sd_bus_negotiate_creds() may + also be called after a connection has been set up. Note that, when operating on a connection that is + shared between multiple components of the same program (for example via + sd_bus_default3), it + is highly recommended to only enable additional per message metadata fields, but never disable them + again, in order not to disable functionality needed by other components. Return Value - On success, these functions return 0 or a - positive integer. On failure, they return a negative errno-style - error code. + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. Errors @@ -120,6 +121,24 @@ The bus connection has already been started. + + + -EINVAL + + An argument is invalid. + + + + -ENOPKG + + The bus cannot be resolved. + + + + -ECHILD + + The bus was created in a different process. + @@ -133,7 +152,7 @@ systemd1, sd-bus3, sd_bus_start3, - sd_bus_message_can_send3, + sd_bus_can_send3, sd_bus_message_get_monotonic_usec3, sd_bus_message_get_realtime_usec3, sd_bus_message_get_seqnum3, diff --git a/man/sd_bus_new.xml b/man/sd_bus_new.xml index 7771a78d8..ceca3350f 100644 --- a/man/sd_bus_new.xml +++ b/man/sd_bus_new.xml @@ -87,14 +87,12 @@ or a related call, and then start the connection with sd_bus_start3. - In most cases, it is a better idea to invoke + In most cases, it is better to use sd_bus_default_user3, sd_bus_default_system3 - or related calls instead of the more low-level - sd_bus_new() and - sd_bus_start(). The higher-level calls not - only allocate a bus object but also start the connection to a - well-known bus in a single function invocation. + or related calls instead of the more low-level sd_bus_new() and + sd_bus_start(). The higher-level functions not only allocate a bus object but also + start the connection to a well-known bus in a single function call. sd_bus_ref() increases the reference counter of bus by one. diff --git a/man/sd_bus_query_sender_creds.xml b/man/sd_bus_query_sender_creds.xml new file mode 100644 index 000000000..54cd81771 --- /dev/null +++ b/man/sd_bus_query_sender_creds.xml @@ -0,0 +1,133 @@ + + + + + + + + sd_bus_query_sender_creds + systemd + + + + sd_bus_query_sender_creds + 3 + + + + sd_bus_query_sender_creds + sd_bus_query_sender_privilege + + Query bus message sender credentials/privileges + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_query_sender_creds + sd_bus_message *m + uint64_t mask + sd_bus_creds **creds + + + + sd_bus_error* sd_bus_query_sender_privilege + sd_bus_message *m + int capability + + + + + + Description + + sd_bus_query_sender_creds() returns the credentials of the message + m. The mask parameter is a combo of + SD_BUS_CREDS_* flags that indicate which credential info the caller is + interested in. See + sd_bus_creds_new_from_pid3 + for a list of possible flags. First, this message checks if the requested credentials are attached to the + message itself. If not but the message contains the pid of the sender, this function tries to figure out + the missing credentials via other means (starting from the pid). If the pid isn't available but the + message has a sender, this function calls + sd_bus_get_name_creds3 + to get the requested credentials. If the message has no sender (when a direct connection is used), this + function calls + sd_bus_get_owner_creds3 + to get the requested credentials. On success, the requested credentials are stored in + creds. Ownership of the credentials object in creds is + transferred to the caller and should be freed by calling + sd_bus_creds_unref3. + + + sd_bus_query_sender_privilege() checks if the message m + has the requested privileges. If capability is a non-negative integer, this + function checks if the message has the capability with the same value. See + capabilities7 + for a list of capabilities. If capability is a negative integer, this function + returns whether the sender of the message runs as the same user as the receiver of the message, or if the + sender of the message runs as root and the receiver of the message does not run as root. On success and + if the message has the requested privileges, this function returns a positive integer. If the message + does not have the requested privileges, this function returns zero. + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The message m or an output parameter is + NULL. + + + + -ENOTCONN + + The bus of m is not connected. + + + + -ECHILD + + The bus of m was created in a different process. + + + + + -EPERM + + The message m is not sealed. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_creds_new_from_pid3, + sd_bus_get_name_creds3, + sd_bus_get_owner_creds3, + sd_bus_creds_unref3, + capabilities7 + + + diff --git a/man/sd_bus_reply_method_error.xml b/man/sd_bus_reply_method_error.xml index 5a6cef6ba..dcf9ee299 100644 --- a/man/sd_bus_reply_method_error.xml +++ b/man/sd_bus_reply_method_error.xml @@ -19,10 +19,12 @@ sd_bus_reply_method_error sd_bus_reply_method_errorf + sd_bus_reply_method_errorfv sd_bus_reply_method_errno sd_bus_reply_method_errnof + sd_bus_reply_method_errnofv - Reply with an error to a method call + Reply with an error to a D-Bus method call @@ -40,7 +42,15 @@ sd_bus_message *call const char *name const char *format - + ... + + + + int sd_bus_reply_method_errorfv + sd_bus_message *call + const char *name + const char *format + va_list ap @@ -55,7 +65,15 @@ sd_bus_message *call int error const char *format - + ... + + + + int sd_bus_reply_method_errnofv + sd_bus_message *call + int error + const char *format + va_list ap @@ -63,12 +81,12 @@ Description - The sd_bus_reply_method_error() function sends an - error reply to the call message. The error structure - e specifies the error to send, and is used as described in - sd_bus_message_new_error3. - If no reply is expected to call, this function returns - success without sending reply. + The sd_bus_reply_method_error() function sends an error reply to the + call message. The error structure e specifies the + error to send, and is used as described in + sd_bus_message_new_method_error3. + If no reply is expected to call, this function succeeds without sending a + reply. The sd_bus_reply_method_errorf() is to sd_bus_reply_method_error() what @@ -89,8 +107,9 @@ Return Value - These functions return 0 if the error reply was successfully sent or if - none was expected, and a negative errno-style error code otherwise. + This function returns a non-negative integer if the error reply was successfully sent or + if call does not expect a reply. On failure, it returns a negative + errno-style error code. Errors @@ -101,15 +120,14 @@ -EINVAL - The call message call is + The input parameter call is NULL. - Message call is not a method call message. - + Message call is not a method call message. Message call is not attached to a bus. - The error error parameter to + The error parameter error to sd_bus_reply_method_error is not set, see sd_bus_error_is_set3. @@ -137,7 +155,7 @@ - In addition, any error message returned by + In addition, any error returned by sd_bus_send1 may be returned. diff --git a/man/sd_bus_reply_method_return.xml b/man/sd_bus_reply_method_return.xml new file mode 100644 index 000000000..a6052c61f --- /dev/null +++ b/man/sd_bus_reply_method_return.xml @@ -0,0 +1,121 @@ + + + + + + + + sd_bus_reply_method_return + systemd + + + + sd_bus_reply_method_return + 3 + + + + sd_bus_reply_method_return + sd_bus_reply_method_returnv + + Reply to a D-Bus method call + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_reply_method_return + sd_bus_message *call + const char *types + ... + + + + int sd_bus_reply_method_returnv + sd_bus_message *call + const char *types + va_list ap + + + + + + Description + + sd_bus_reply_method_return() sends a reply to the + call message. The type string types and the + arguments that follow it must adhere to the format described in + sd_bus_message_append3. + If no reply is expected to call, this function succeeds without sending a + reply. + + + + Return Value + + On success, this function returns a non-negative integer. On failure, it returns a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The input parameter call is + NULL. + + Message call is not a method call message. + + + Message call is not attached to a bus. + + + + + -EPERM + + Message call has been sealed. + + + + + -ENOTCONN + + The bus to which message call is attached is not + connected. + + + + -ENOMEM + + Memory allocation failed. + + + + In addition, any error returned by + sd_bus_send1 + may be returned. + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_new_method_return3 + + + + diff --git a/man/sd_bus_request_name.xml b/man/sd_bus_request_name.xml index 7229ef517..f8a49c006 100644 --- a/man/sd_bus_request_name.xml +++ b/man/sd_bus_request_name.xml @@ -28,6 +28,8 @@ #include <systemd/sd-bus.h> + + int sd_bus_request_name sd_bus *bus diff --git a/man/sd_bus_send.xml b/man/sd_bus_send.xml new file mode 100644 index 000000000..2cdf436db --- /dev/null +++ b/man/sd_bus_send.xml @@ -0,0 +1,149 @@ + + + + + + + + sd_bus_send + systemd + + + + sd_bus_send + 3 + + + + sd_bus_send + sd_bus_send_to + + Queue a D-Bus message for transfer + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_send + sd_bus *bus + sd_bus_message *m + uint64_t *cookie + + + + int sd_bus_send_to + sd_bus *bus + sd_bus_message *m + const char *destination + uint64_t *cookie + + + + + + Description + + sd_bus_send() queues the bus message object m for + transfer. If bus is NULL, the bus that + m is attached to is used. bus only needs to be set when the + message is sent to a different bus than the one it's attached to, for example when forwarding messages. + If the output parameter cookie is not NULL, it is set to the + message identifier. This value can later be used to match incoming replies to their corresponding + messages. If cookie is set to NULL and the message is not + sealed, sd_bus_send() assumes the message m doesn't expect a + reply and adds the necessary headers to indicate this. + + Note that in most scenarios, sd_bus_send() should not be called + directly. Instead, use higher level functions such as + sd_bus_call_method3 and + sd_bus_reply_method_return3 + which call sd_bus_send() internally. + + sd_bus_send_to() is a shorthand for sending a message to a specific + destination. It's main use case is to simplify sending unicast signal messages (signals that only have a + single receiver). It's behavior is similar to calling + sd_bus_message_set_destination3 + followed by calling sd_bus_send(). + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The input parameter m is NULL. + + + + + -EOPNOTSUPP + + The bus connection does not support sending file descriptors. + + + + + -ECHILD + + The bus connection was allocated in a parent process and is being reused in a child + process after fork(). + + + + -ENOBUFS + + The bus connection's write queue is full. + + + + -ENOTCONN + + The input parameter bus is + NULL or the bus is not connected. + + + + -ECONNRESET + + The bus connection was closed while waiting for the response. + + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call_method3, + sd_bus_message_set_destination3, + sd_bus_reply_method_return3 + + + + diff --git a/man/sd_bus_set_address.xml b/man/sd_bus_set_address.xml new file mode 100644 index 000000000..8404da53f --- /dev/null +++ b/man/sd_bus_set_address.xml @@ -0,0 +1,188 @@ + + + + + + + + sd_bus_set_address + systemd + + + + sd_bus_set_address + 3 + + + + sd_bus_set_address + sd_bus_get_address + sd_bus_set_exec + + Set or query the address of the bus connection + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_set_address + sd_bus *bus + const char *address + + + + int sd_bus_get_address + sd_bus *bus + const char **address + + + + int sd_bus_set_exec + sd_bus *bus + const char *path + char *const *argv + + + + + + Description + + sd_bus_set_address() configures a list of addresses of bus brokers to try to + connect to from a subsequent + sd_bus_start3 call. + The argument is a ;-separated list of addresses to try. Each item must be one of the + following: + + + + + A unix socket address specified as + unix:guid=guid,path=path or + unix:guid=guid,abstract=path. + Exactly one of the path= and abstract= keys must be present, + while guid= is optional. + + + + A TCP socket address specified as + tcp:[guid=guid,][host=host][,port=port][,family=family]. + One or both of the host= and port= keys must be present, while + the rest is optional. family may be either or + . + + + + An executable to spawn specified as + unixexec:guid=guid,path=path,argv1=argument,argv2=argument,.... + The path= key must be present, while guid= is optional. + + + + A machine (container) to connect to specified as + x-machine-unix:guid=guid,machine=machine,pid=pid. + Exactly one of the machine= and pid= keys must be present, + while guid= is optional. machine is the name of a local + container. See + machinectl1 for + more information about the "machine" concept. machine=.host may be used to specify + the host machine. A connection to the standard system bus socket inside of the specified machine will + be created. + + + + In all cases, parameter guid is an identifier of the remote peer, in the + syntax accepted by + sd_id128_from_string3. + If specified, the identifier returned by the peer after the connection is established will be checked and + the connection will be rejected in case of a mismatch. + + Note that the addresses passed to sd_bus_set_address() may not be verified + immediately. If they are invalid, an error may be returned e.g. from a subsequent call to + sd_bus_start3. + + + sd_bus_get_address() returns any previously set addresses. In addition to + being explicitly set by sd_bus_set_address(), the address will also be set + automatically by + sd_bus_open3 and + similar calls, based on environment variables or built-in defaults. + + sd_bus_set_exec is a shorthand function for setting a + unixexec address that spawns the given executable with the given arguments. + If argv is NULL, the given executable is spawned + without any extra arguments. + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a negative + errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The input parameters bus or address are NULL. + + + + + -ENOPKG + + The bus object bus could not be resolved. + + + + + -EPERM + + The input parameter bus is in a wrong state + (sd_bus_set_address() may only be called once on a newly-created bus object). + + + + + -ECHILD + + The bus object bus was created in a different + process. + + + + + -ENODATA + + The bus object bus has no address configured. + + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_new3, + sd_bus_start3, + systemd-machined.service8, + machinectl1 + + + + diff --git a/man/sd_bus_set_close_on_exit.xml b/man/sd_bus_set_close_on_exit.xml index 751fc0a72..64ca35644 100644 --- a/man/sd_bus_set_close_on_exit.xml +++ b/man/sd_bus_set_close_on_exit.xml @@ -20,7 +20,8 @@ sd_bus_set_close_on_exit sd_bus_get_close_on_exit - Control whether to close the bus connection during the event loop exit phase + Control whether to close the bus connection during the event loop exit phase + @@ -44,30 +45,35 @@ Description - sd_bus_set_close_on_exit() may be used to enable or disable whether the bus connection - is automatically flushed (as in - sd_bus_flush3) and closed (as in - sd_bus_close3) during the exit - phase of the event loop. This logic only applies to bus connections that are attached to an - sd-event3 event loop, see - sd_bus_attach_event3. By default - this mechanism is enabled and makes sure that any pending messages that have not been written to the bus connection - are written out when the event loop is shutting down. In some cases this behaviour is not desirable, for example - when the bus connection shall remain usable until after the event loop exited. If b is - true, the feature is enabled (which is the default), otherwise disabled. + sd_bus_set_close_on_exit() may be used to enable or disable whether + the bus connection is automatically flushed (as in + sd_bus_flush3) + and closed (as in + sd_bus_close3) + during the exit phase of the event loop. This logic only applies to bus connections that are + attached to an + sd-event3 + event loop, see + sd_bus_attach_event3. + By default this mechanism is enabled and makes sure that any pending messages that have not been + written to the bus connection are written out when the event loop is shutting down. In some + cases this behaviour is not desirable, for example when the bus connection shall remain usable + until after the event loop exited. If b is true, the feature is enabled + (which is the default), otherwise disabled. - sd_bus_get_close_on_exit() may be used to query the current setting of this feature. It - returns zero when the feature is disabled, and positive if enabled. + sd_bus_get_close_on_exit() may be used to query the current setting + of this feature. It returns zero when the feature is disabled, and positive if enabled. Return Value - On success, sd_bus_set_close_on_exit() returns 0 or a positive integer. On failure, it returns a negative errno-style - error code. + On success, sd_bus_set_close_on_exit() returns a non-negative + integer. On failure, it returns a negative errno-style error code. - sd_bus_get_close_on_exit() returns 0 if the feature is currently turned off or a - positive integer if it is on. On failure, it returns a negative errno-style error code. + sd_bus_get_close_on_exit() returns 0 if the feature is currently + disabled or a positive integer if it is enabled. On failure, it returns a negative errno-style + error code. Errors @@ -78,7 +84,8 @@ -ECHILD - The bus connection has been created in a different process. + The bus connection was created in a different process. + @@ -98,5 +105,4 @@ sd_event_add_exit3 - diff --git a/man/sd_bus_set_connected_signal.xml b/man/sd_bus_set_connected_signal.xml index edb0df205..b025112b6 100644 --- a/man/sd_bus_set_connected_signal.xml +++ b/man/sd_bus_set_connected_signal.xml @@ -20,7 +20,7 @@ sd_bus_set_connected_signal sd_bus_get_connected_signal - Control emmission of local connection establishment signal on bus connections + Control emission of local connection establishment signal on bus connections diff --git a/man/sd_bus_set_description.xml b/man/sd_bus_set_description.xml index 3c5580e27..bd3ec7886 100644 --- a/man/sd_bus_set_description.xml +++ b/man/sd_bus_set_description.xml @@ -19,9 +19,14 @@ sd_bus_set_description sd_bus_get_description sd_bus_set_anonymous + sd_bus_is_anonymous sd_bus_set_trusted + sd_bus_is_trusted sd_bus_set_allow_interactive_authorization sd_bus_get_allow_interactive_authorization + sd_bus_get_scope + sd_bus_get_tid + sd_bus_get_unique_name Set or query properties of a bus object @@ -48,12 +53,22 @@ int b + + int sd_bus_is_anonymous + sd_bus *bus + + int sd_bus_set_trusted sd_bus *bus int b + + int sd_bus_is_trusted + sd_bus *bus + + int sd_bus_set_allow_interactive_authorization sd_bus *bus @@ -64,60 +79,101 @@ int sd_bus_get_allow_interactive_authorization sd_bus *bus + + + int sd_bus_get_scope + sd_bus *bus + const char **scope + + + + int sd_bus_get_tid + sd_bus *bus + pid_t *tid + + + + int sd_bus_get_unique_name + sd_bus *bus + const char **unique + Description - sd_bus_set_description() sets the description string - that is used in logging to the specified string. The string is copied internally - and freed when the bus object is deallocated. The - description argument may be NULL, in - which case the description is unset. This function must be called before the bus - has been started. + sd_bus_set_description() sets the description string that is used in + logging to the specified string. The string is copied internally and freed when the bus object + is deallocated. The description argument may be + NULL, in which case the description is unset. This function must be called + before the bus is started. - sd_bus_get_description() returns a description string - in description. This string may have been previously set - with sd_bus_set_description() or + sd_bus_get_description() returns a description string in + description. This string may have been previously set with + sd_bus_set_description() or sd_bus_open_with_description3 - or similar. If not set this way, a default string like system - or user will be returned for the system or user buses, - and NULL otherwise. + or similar. If not set this way, a default string like system or + user will be returned for the system or user buses, and + NULL otherwise. - sd_bus_set_anonymous() enables or disables "anonymous - authentication", i.e. lack of authentication, of the bus peer. This function must - be called before the bus has been started. See the Authentication - Mechanisms section of the D-Bus specification for details. + sd_bus_set_anonymous() enables or disables "anonymous authentication", + i.e. lack of authentication, of the bus peer. This function must be called before the bus is + started. See the + + Authentication Mechanisms section of the D-Bus specification for details. + + sd_bus_is_anonymous() returns true if the bus connection allows + anonymous authentication (in the sense described in previous paragraph). sd_bus_set_trusted() sets the "trusted" state on the - bus object. If true, all connections on the bus are - trusted and access to all privileged and unprivileged methods is granted. This - function must be called before the bus has been started. + bus object. If true, all connections on the bus are trusted and access to + all privileged and unprivileged methods is granted. This function must be called before the bus + is started. - sd_bus_set_allow_interactive_authorization() - enables or disables interactive authorization for method calls. If true, - messages are marked with the + sd_bus_is_trusted() returns true if the bus connection is trusted (in + the sense described in previous paragraph). + + sd_bus_set_allow_interactive_authorization() enables or disables + interactive authorization for method calls. If true, messages are marked with the ALLOW_INTERACTIVE_AUTHORIZATION flag specified by the - D-Bus - specification, informing the receiving side that the caller is prepared to - wait for interactive authorization, which might take a considerable time to - complete. If this flag is set, the user may be queried for passwords or - confirmation via polkit or a - similar framework. + D-Bus + specification, informing the receiving side that the caller is prepared to wait for interactive + authorization, which might take a considerable time to complete. If this flag is set, the user + may be queried for passwords or confirmation via + polkit or a similar + framework. - sd_bus_get_allow_interactive_authorization() returns - true if interactive authorization is allowed and false if not. + sd_bus_get_allow_interactive_authorization() returns true if + interactive authorization is allowed and false if not. + + sd_bus_get_scope() stores the scope of the given bus object in + scope. The scope of the system bus is system. The + scope of a user session bus is user. If the given bus object is not the + system or a user session bus, sd_bus_get_scope() returns an error. + + sd_bus_get_tid() stores the kernel thread id of the thread associated + with the given bus object in tid. If bus is a + default bus object obtained by calling one of the functions of the + sd_bus_default3 + family of functions, it stores the thread id of the thread the bus object was created in. + Otherwise, if the bus object is attached to an event loop, it stores the thread id of the + thread the event loop object was created in. If bus is not a default bus + object and is not attached to an event loop, sd_bus_get_tid() returns an + error. + + sd_bus_get_unique_name() stores the unique name of the bus object on + the bus in unique. See + + The D-Bus specification for more information on bus names. Note that the caller does not + own the string stored in unique and should not free it. Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative - errno-style error code. + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. Errors @@ -154,8 +210,21 @@ Memory allocation failed. - + + -ENODATA + + The bus object passed to sd_bus_get_scope() was not a + system or user session bus. + + + + -ENXIO + + The bus object passed to sd_bus_get_tid() was not a + default bus object and is not attached to an event loop. + + diff --git a/man/sd_bus_set_exit_on_disconnect.xml b/man/sd_bus_set_exit_on_disconnect.xml new file mode 100644 index 000000000..8bd904ba4 --- /dev/null +++ b/man/sd_bus_set_exit_on_disconnect.xml @@ -0,0 +1,114 @@ + + + + + + + + sd_bus_set_exit_on_disconnect + systemd + + + + sd_bus_set_exit_on_disconnect + 3 + + + + sd_bus_set_exit_on_disconnect + sd_bus_get_exit_on_disconnect + + Control the exit behavior when the bus object disconnects + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_set_exit_on_disconnect + sd_bus *bus + int b + + + + int sd_bus_get_exit_on_disconnect + sd_bus *bus + + + + + + Description + + sd_bus_set_exit_on_disconnect() may be used to configure the exit + behavior when the given bus object disconnects. If b is zero, no special + logic is executed when the bus object disconnects. If b is non-zero, the + behavior on disconnect depends on whether the bus object is attached to an event loop or not. If + the bus object is attached to an event loop (see + sd_bus_attach_event3), + the event loop is closed when the bus object disconnects (as if calling + sd_event_exit3). + Otherwise, + exit3 + is called. The exit code passed to sd_event_exit() and + exit() is EXIT_FAILURE. If the bus object has already + disconnected when enabling the exit behavior, the exit behavior is executed immediately. By + default, the exit behavior is disabled. + + sd_bus_get_exit_on_disconnect() returns whether the exit on + disconnect behavior is enabled for the given bus object. + + + + Return Value + + On success, sd_bus_set_exit_on_disconnect() returns a non-negative + integer. On failure, it returns a negative errno-style error code. + + sd_bus_get_exit_on_disconnect() returns a positive integer if the + exit on disconnect behavior is enabled. Otherwise, it returns zero. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + A required parameter was NULL. + + + + -ENOPKG + + The bus object could not be resolved. + + + + -ECHILD + + The bus connection was created in a different process. + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_attach_event3, + sd-event3, + sd_event_exit3 + + + diff --git a/man/sd_bus_set_method_call_timeout.xml b/man/sd_bus_set_method_call_timeout.xml new file mode 100644 index 000000000..006020aae --- /dev/null +++ b/man/sd_bus_set_method_call_timeout.xml @@ -0,0 +1,103 @@ + + + + + + + + sd_bus_set_method_call_timeout + systemd + + + + sd_bus_set_method_call_timeout + 3 + + + + sd_bus_set_method_call_timeout + sd_bus_get_method_call_timeout + + Set or query the default D-Bus method call timeout of a bus object + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_set_method_call_timeout + sd_bus *bus + uint64_t usec + + + + int sd_bus_get_method_call_timeout + sd_bus *bus + uint64_t *ret + + + + + + Description + + sd_bus_set_method_call_timeout() sets the default D-Bus method call + timeout of bus to usec microseconds. + + sd_bus_get_method_call_timeout() queries the default D-Bus method + call timeout of bus. If no method call timeout was set using + sd_bus_set_method_call_timeout(), the timeout is read from the + $SYSTEMD_BUS_TIMEOUT environment variable. If this environment variable is + unset or does not contain a valid timeout, the implementation falls back to a predefined method + call timeout of 25 seconds. Note that $SYSTEMD_BUS_TIMEOUT is read once and + cached so callers should not rely on being able to change the default method call timeout at + runtime by changing the value of $SYSTEMD_BUS_TIMEOUT. Instead, call + sd_bus_set_method_call_timeout() to change the default method call timeout. + + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The parameters bus or ret + are NULL. + + + + -ENOPKG + + Bus object bus could not be resolved. + + + + + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call3 + + + + diff --git a/man/sd_bus_set_property.xml b/man/sd_bus_set_property.xml new file mode 100644 index 000000000..411ccad28 --- /dev/null +++ b/man/sd_bus_set_property.xml @@ -0,0 +1,176 @@ + + + + + + + + sd_bus_set_property + systemd + + + + sd_bus_set_property + 3 + + + + sd_bus_set_property + sd_bus_set_propertyv + sd_bus_get_property + sd_bus_get_property_trivial + sd_bus_get_property_string + sd_bus_get_property_strv + + Set or query D-Bus service properties + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_set_property + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + sd_bus_message **reply + const char *type + ... + + + + int sd_bus_set_propertyv + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + sd_bus_message **reply + const char *type + va_list ap + + + + int sd_bus_get_property + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + sd_bus_message **reply + const char *type + + + + int sd_bus_get_property_trivial + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + char type + void *ret_ptr + + + + int sd_bus_get_property_string + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + char **ret + + + + int sd_bus_get_property_strv + sd_bus *bus + const char *destination + const char *path + const char *interface + const char *member + sd_bus_error *ret_error + char ***ret + + + + + + Description + + These functions set or query D-Bus properties. D-Bus properties are service fields exposed + via the org.freedesktop.DBus.Properties interface. Under the hood, these + functions call methods of the org.freedesktop.DBus.Properties interface and + as a result their semantics are similar to + sd_bus_call_method3. + + + sd_bus_set_property() sets a D-Bus property. On success, the response + is stored in reply. If setting the property fails or an internal error + occurs, an error is returned and an extended description of the error is optionally stored in + ret_error if it is not NULL. + type and the arguments that follow it describe the new value of the + property and must follow the format described in + sd_bus_message_append3. + + + sd_bus_set_propertyv() is equivalent to + sd_bus_set_property(), except that it is called with a + va_list instead of a variable number of arguments. + + sd_bus_get_property() queries a D-Bus property. If retrieving the + property fails or an internal error occurs, an error is returned and an extended description of + the error is optionally stored in ret_error if it is not + NULL. On success, the property is stored in reply. + type describes the property type and must follow the format described in + sd_bus_message_append3. + + + sd_bus_get_property_trivial(), + sd_bus_get_property_string() and + sd_bus_get_property_strv() are shorthands for + sd_bus_get_property() that are used to query basic, string and string + vector properties respectively. The caller is responsible for freeing the string and string + vector results stored in ret by + sd_bus_get_property_string() and + sd_bus_get_property_strv(). + + + + Return Value + + On success, these functions return a non-negative integer. On failure, they return a + negative errno-style error code. + + + Errors + + See the + sd_bus_call_method3 + man page for a list of possible errors + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_call_method3 + + + + diff --git a/man/sd_bus_set_server.xml b/man/sd_bus_set_server.xml new file mode 100644 index 000000000..625dfd446 --- /dev/null +++ b/man/sd_bus_set_server.xml @@ -0,0 +1,193 @@ + + + + + + + + sd_bus_set_server + systemd + + + + sd_bus_set_server + 3 + + + + sd_bus_set_server + sd_bus_is_server + sd_bus_get_bus_id + sd_bus_set_bus_client + sd_bus_is_bus_client + sd_bus_set_monitor + sd_bus_is_monitor + + Configure connection mode for a bus object + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_set_server + sd_bus *bus + int b + sd_id128_t id + + + + int sd_bus_is_server + sd_bus *bus + + + + int sd_bus_get_bus_id + sd_bus *bus + sd_id128_t *id + + + + int sd_bus_set_bus_client + sd_bus *bus + int b + + + + int sd_bus_is_bus_client + sd_bus *bus + + + + int sd_bus_set_monitor + sd_bus *bus + int b + + + + int sd_bus_is_monitor + sd_bus *bus + + + + + + Description + + sd_bus_set_server() configures the bus object as a server for direct D-Bus + connections. b enables/disables the server mode. If zero, the server mode is + disabled. Otherwise, the server mode is enabled. Configuring a bus object as a server is required to + allow establishing direct connections between two peers without going via the D-Bus daemon. + id must contain a 128-bit integer id for the server. If clients add a guid field + to their D-Bus address string, the server id must match this guid or the D-Bus authentication handshake + will fail. If no specific id is defined for the server, + sd_id128_randomize3 + can be used to generate a random id instead. + + sd_bus_is_server() returns whether the server mode is enabled for the given + bus object. + + sd_bus_get_bus_id() stores the D-Bus server id configured using + sd_bus_set_server() (for server bus objects) or received during D-Bus authentication + (for client bus objects) in id. + + sd_bus_set_bus_client() configures the bus object as a D-Bus daemon client. + b enables/disables the client mode. If zero, the client mode is disabled and the + bus object should connect directly to a D-Bus server. Otherwise, the client mode is enabled and the bus + object should connect to a D-Bus daemon. When connecting to an existing bus using any of the functions in + the sd_bus_open3 + family of functions or any of the functions in the + sd_bus_default3 family + of functions, the bus object is automatically configured as a bus client. However, when connecting to a + D-Bus daemon by calling + sd_bus_set_address3 + followed by + sd_bus_start3, the bus + object should be manually configured as a bus client using sd_bus_set_bus_client(). + By default, a bus object is not configured as a D-Bus daemon client. + + sd_bus_is_bus_client() returns whether the client mode is enabled/disabled for + the given bus object. + + sd_bus_set_monitor() configures the bus object as a D-Bus monitor object. + b enables/disables the monitor mode. If zero, the monitor mode is disabled. If + non-zero, the monitor mode is enabled. When the monitor mode is enabled, no messages may be sent via the + bus object and it may not expose any objects on the bus. To start monitoring messages, call the + org.freedesktop.DBus.Monitoring.BecomeMonitor method of the D-Bus daemon and pass + a list of matches indicating which messages to intercept. See + + The D-Bus specification for more information. + + sd_bus_is_monitor() returns whether the monitor mode is enabled/disabled for + the given bus object. + + + + + Return Value + + On success, sd_bus_set_server(), + sd_bus_get_bus_id(), sd_bus_set_bus_client() and + sd_bus_set_monitor() return a non-negative integer. On failure, they return a + negative errno-style error code. + + sd_bus_is_server(), sd_bus_is_bus_client() and + sd_bus_is_monitor() return a positive integer when the server or client mode is + enabled, respectively. Otherwise, they return zero. + + + Errors + + Returned errors may indicate the following problems: + + + + -ECHILD + + The bus connection has been created in a different process. + + + + -EPERM + + The bus connection has already been started. + + + + -ENOPKG + + The bus cannot be resolved. + + + + -EINVAL + + A required parameter was NULL or + b was zero and id did not equal + SD_ID128_NULL. + + + + -ENOTCONN + + The bus is not connected. + + + + + + + + + See Also + + + systemd1, + sd-bus3 + + + + diff --git a/man/sd_bus_set_watch_bind.xml b/man/sd_bus_set_watch_bind.xml index cbdc7dd1e..5638cdc0a 100644 --- a/man/sd_bus_set_watch_bind.xml +++ b/man/sd_bus_set_watch_bind.xml @@ -48,7 +48,7 @@ socket binding for a bus connection object. If the b is true, the feature is enabled, otherwise disabled (which is the default). When enabled, and the selected bus address refers to an AF_UNIX socket in the file system which does not exist while the connection attempt is made an - inotify7 watch is installed on + inotify7 watch is installed on it, waiting for the socket to appear. As soon as the socket appears the connection is made. This functionality is useful in particular in early-boot programs that need to run before the system bus is available, but want to connect to it the instant it may be connected to. @@ -56,18 +56,18 @@ sd_bus_get_watch_bind() may be used to query the current setting of this feature. It returns zero when the feature is disabled, and positive if enabled. - Note that no timeout is applied while it is waited for the socket to appear. This means that any synchronous - remote operation (such as + Note that no timeout is applied while we wait for the socket to appear. This means that any + synchronous remote operation (such as sd_bus_call3, sd_bus_add_match3 or - sd_bus_request_name3), that is - used on a connection with this feature enabled that is not established yet might block unbounded if the socket is - never created. However, asynchronous remote operations (such as + sd_bus_request_name3), + that is used on a connection with this feature enabled that hasn't been established yet, might block + forever if the socket is never created. However, asynchronous remote operations (such as sd_bus_send3, - sd_bus_add_match_async3 or - sd_bus_request_match_async3) do - not block in this case, and safely enqueue the requested operations to be dispatched the instant the connection is - set up. + sd_bus_call_async3, + sd_bus_add_match_async3) + do not block in this case, and safely enqueue the requested operations to be dispatched the instant the + connection is set up. Use sd_bus_is_ready3 to determine whether the connection is fully established, i.e. whether the peer socket has been bound, connected to @@ -106,7 +106,7 @@ systemd1, sd-bus3, - inotify7, + inotify7, sd_bus_call3, sd_bus_add_match3, sd_bus_request_name3, diff --git a/man/sd_bus_slot_get_bus.xml b/man/sd_bus_slot_get_bus.xml new file mode 100644 index 000000000..26541a9d9 --- /dev/null +++ b/man/sd_bus_slot_get_bus.xml @@ -0,0 +1,90 @@ + + + + + + + sd_bus_slot_get_bus + systemd + + + + sd_bus_slot_get_bus + 3 + + + + sd_bus_slot_get_bus + sd_bus_slot_get_current_handler + sd_bus_slot_get_current_message + sd_bus_slot_get_current_userdata + + Query information attached to a bus slot object + + + + + #include <systemd/sd-bus.h> + + + + + sd_bus *sd_bus_slot_get_bus + sd_bus_slot *slot + + + + sd_bus_message_handler_t sd_bus_slot_get_current_handler + + sd_bus_slot *slot + + + + sd_bus_message *sd_bus_slot_get_current_message + sd_bus_slot *slot + + + + void *sd_bus_slot_get_current_userdata + sd_bus_slot *slot + + + + + + Description + + sd_bus_slot_get_bus() returns the bus object that message + slot is attached to. + + sd_bus_slot_get_current_handler(), + sd_bus_slot_get_current_message() and + sd_bus_slot_get_current_userdata() return the current handler, message and + userdata respectively of the bus slot is attached to if we're currently + executing the callback associated with slot. + + + + Return Value + + sd_bus_slot_get_bus() always returns the bus object. + + On success, sd_bus_slot_get_current_handler(), + sd_bus_slot_get_current_message() and + sd_bus_slot_get_current_userdata() return the requested object. On failure, + they return NULL. + + + + + + See Also + + + systemd1, + sd-bus3, + + + + diff --git a/man/sd_bus_slot_ref.xml b/man/sd_bus_slot_ref.xml index c73f3c9e2..ef144ece6 100644 --- a/man/sd_bus_slot_ref.xml +++ b/man/sd_bus_slot_ref.xml @@ -18,7 +18,6 @@ sd_bus_slot_ref sd_bus_slot_unref sd_bus_slot_unrefp - sd_bus_slot_get_bus Create and destroy references to a bus slot object @@ -41,11 +40,6 @@ void sd_bus_slot_unrefp sd_bus_slot **slotp - - - sd_bus *sd_bus_slot_get_bus - sd_bus_slot *m - @@ -72,11 +66,7 @@ execute no operation if the passed in bus object address is NULL. sd_bus_slot_unrefp() will first dereference its argument, which must not be NULL, and will execute no operation if - that is NULL. - - - sd_bus_slot_get_bus() returns the bus object that message - slot is attached to. + that is NULL. @@ -85,8 +75,6 @@ sd_bus_slot_ref() always returns the argument. sd_bus_slot_unref() always returns NULL. - - sd_bus_slot_get_bus() always returns the bus object. @@ -99,7 +87,6 @@ sd-bus3, sd_bus_new3, sd_bus_message_new3, - sd_bus_slot_new_signal3, sd_bus_call_method_async3 diff --git a/man/sd_bus_slot_set_destroy_callback.xml b/man/sd_bus_slot_set_destroy_callback.xml index c6fe72e37..c2a0876e2 100644 --- a/man/sd_bus_slot_set_destroy_callback.xml +++ b/man/sd_bus_slot_set_destroy_callback.xml @@ -23,7 +23,7 @@ sd_bus_track_get_destroy_callback sd_bus_destroy_t - Define the callback function for resource cleanup. + Define the callback function for resource cleanup diff --git a/man/sd_bus_slot_set_floating.xml b/man/sd_bus_slot_set_floating.xml index f63907aa4..ecfc07951 100644 --- a/man/sd_bus_slot_set_floating.xml +++ b/man/sd_bus_slot_set_floating.xml @@ -19,7 +19,7 @@ sd_bus_slot_set_floating sd_bus_slot_get_floating - Control whether a bus slot object is "floating". + Control whether a bus slot object is "floating" diff --git a/man/sd_bus_start.xml b/man/sd_bus_start.xml new file mode 100644 index 000000000..0be07f477 --- /dev/null +++ b/man/sd_bus_start.xml @@ -0,0 +1,124 @@ + + + + + + + + sd_bus_start + systemd + + + + sd_bus_start + 3 + + + + sd_bus_start + + Initiate a bus connection to the D-bus broker daemon + + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_start + sd_bus *bus + + + + + + Description + + sd_bus_start() connects an existing bus connection object to the D-Bus + broker daemon, usually + dbus-daemon1 + or + dbus-broker1. + The mechanism to use for the connection must be configured before the call to + sd_bus_start(), using one of + sd_bus_set_address3, + sd_bus_set_fd3, or + sd_bus_set_exec3. + sd_bus_start() will open the connection socket or spawn the executable as + needed, and asynchronously start a org.freedesktop.DBus.Hello() call. The + answer to the Hello call will be processed later from + sd_bus_process3. If + opening of the connection or queuing of the asynchronous call fail, the connection will be closed with + sd_bus_close3. + + In most cases, it is better to use + sd_bus_default_user3, + sd_bus_default_system3 + or related calls instead of the more low-level sd_bus_new() and + sd_bus_start(). The higher-level functions not only allocate a bus object but also + start the connection to a well-known bus in a single function call. + + + + Return Value + + On success, this function returns a non-negative integer. On failure, it returns a negative + errno-style error code. + + + Errors + + + + -EINVAL + + The input parameter bus is NULL. + + + + + -ENOPKG + + Bus object bus could not be resolved. + + + + + -EPERM + + The input parameter bus is in a wrong state + (sd_bus_start() may only be called once on a newly-created bus object). + + + + + -ECHILD + + The bus object bus was created in a different + process. + + + + + In addition, other connection-related errors may be returned. See + sd_bus_send3. + + + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_default3, + sd_bus_call_async3 + + + + diff --git a/man/sd_event_new.xml b/man/sd_event_new.xml index 3db39c9d0..0e572c40e 100644 --- a/man/sd_event_new.xml +++ b/man/sd_event_new.xml @@ -131,7 +131,7 @@ automatically as the code block is left: { - __attribute__((cleanup(sd_event_unrefp)) sd_event *event = NULL; + __attribute__((cleanup(sd_event_unrefp))) sd_event *event = NULL; int r; … r = sd_event_default(&event); diff --git a/man/sd_event_source_set_destroy_callback.xml b/man/sd_event_source_set_destroy_callback.xml index 2ffca9ec3..3df926b86 100644 --- a/man/sd_event_source_set_destroy_callback.xml +++ b/man/sd_event_source_set_destroy_callback.xml @@ -21,7 +21,7 @@ sd_event_source_get_destroy_callback sd_event_destroy_t - Define the callback function for resource cleanup. + Define the callback function for resource cleanup diff --git a/man/sd_hwdb_get.xml b/man/sd_hwdb_get.xml new file mode 100644 index 000000000..dbaaf6292 --- /dev/null +++ b/man/sd_hwdb_get.xml @@ -0,0 +1,157 @@ + + + + + + + sd_hwdb_get + systemd + + + + sd_hwdb_get + 3 + + + + sd_hwdb_get + sd_hwdb_seek + sd_hwdb_enumerate + SD_HWDB_FOREACH_PROPERTY + + Seek to a location in hwdb or access entries + + + + + #include <systemd/sd-hwdb.h> + + + int sd_hwdb_get + sd_hwdb *hwdb + const char *modalias + const char *key + const char **value + + + + int sd_hwdb_seek + sd_hwdb *hwdb + const char *modalias + + + + int sd_hwdb_enumerate + sd_hwdb *hwdb + const char **key + const char **value + + + + SD_HWDB_FOREACH_PROPERTY + hwdb + modalias + key + value + + + + + + Description + + sd_hwdb_get() queries the hwdb object created earlier + with sd_hwdb_new3 for + entries matching the specified string modalias, and returns the value + corresponding to the key key. The value is returned as a + NUL-terminated string in value. It must not be modified by + the caller and is valid as long as a reference to hwdb is kept. When multiple + patterns in the database match modalias, the one with the highest priority is + used. See hwdb7 for + details. + + sd_hwdb_seek() selects entries matching the specified string + modalias. Subsequent queries with sd_hwdb_enumerate() will + access the key-value pairs for that string. + + sd_hwdb_enumerate() returns (in turn) all the key-value pairs defined for the + string used with sd_hwdb_seek(). Each pair is returned as + NUL-terminated strings in key and + value. The strings must not be modified by the caller and are valid as long as a + reference to hwdb is kept. When multiple patterns in the database match + modalias, the combination of all matching key-value pairs is used. See + hwdb7 for + details. + + The SD_HWDB_FOREACH_PROPERTY macro combines + sd_hwdb_seek() and sd_hwdb_enumerate(). No error handling is + performed and iteration simply stops on error. See the example below. + + + + Return Value + + On success, sd_hwdb_get() and sd_hwdb_seek() return a + non-negative integer. On failure, they return a negative errno-style error code. + + sd_hwdb_enumerate() returns a positive integer if another key-value pair was found or zero if + all entries have already been enumerated. On failure, it returns a negative errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + A parameter is NULL. + + + + -ENOENT + + An entry for the specified modalias was not found. + + + + + -EAGAIN + + sd_hwdb_seek() was not called before + sd_hwdb_enumerate(). + + + + + + + + + Examples + + + Look up hwdb entries for a USB device + + + + The effect is similar to calling systemd-hwdb query usb:v046DpC534. + + + + + + See Also + + + systemd1, + systemd-udevd.service8, + sd-hwdb3, + systemd-hwdb8 + + + + diff --git a/man/sd_hwdb_new.xml b/man/sd_hwdb_new.xml new file mode 100644 index 000000000..8f1c01b73 --- /dev/null +++ b/man/sd_hwdb_new.xml @@ -0,0 +1,121 @@ + + + + + + + sd_hwdb_new + systemd + + + + sd_hwdb_new + 3 + + + + sd_hwdb_new + sd_hwdb_ref + sd_hwdb_unref + + Create a new hwdb object and create or destroy references to it + + + + + #include <systemd/sd-hwdb.h> + + + int sd_hwdb_new + sd_hwdb **hwdb + + + + sd_hwdb* sd_hwdb_ref + sd_hwdb *hwdb + + + + sd_hwdb* sd_hwdb_unref + sd_hwdb *hwdb + + + + + + Description + + sd_hwdb_new() creates a new hwdb object to access the binary hwdb + database. Upon initialization, the file containing the binary representation of the hardware database is + located and opened. The new object is returned in hwdb. + + The hwdb object is reference counted. sd_hwdb_ref() and + sd_hwdb_unref() may be used to get a new reference or destroy an existing reference + to an object. The caller must dispose of the reference acquired with sd_hwdb_new() + by calling sd_hwdb_unref() when done with the object. + + Use + sd_hwdb_seek3, + sd_hwdb_get3, and + sd_hwdb_enumerate3 to + access entries. + + + + Return Value + + On success, sd_hwdb_new() returns a non-negative integer. On + failure, it returns a negative errno-style error code. + + sd_hwdb_ref() always returns the argument. + + + sd_hwdb_unref() always returns NULL. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOENT + + The binary hardware database file could not be located. See + systemd-hwdb8 + for more information. + + + + + -EINVAL + + The located binary hardware database file is in an incompatible format. + + + + + -ENOMEM + + Memory allocation failed. + + + + + + + + + See Also + + + systemd1, + systemd-udevd.service8, + sd-hwdb3, + systemd-hwdb3 + + + + diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml index e665f7343..bb85d8839 100644 --- a/man/sd_id128_get_machine.xml +++ b/man/sd_id128_get_machine.xml @@ -66,7 +66,7 @@ file. This function caches the machine ID internally to make retrieving the machine ID a cheap operation. This ID may be used wherever a unique identifier for the local system is needed. However, it is recommended to use this ID as-is only in trusted environments. In untrusted environments it is recommended to derive an application specific - ID from this machine ID, in an irreversable (cryptographically secure) way. To make this easy + ID from this machine ID, in an irreversible (cryptographically secure) way. To make this easy sd_id128_get_machine_app_specific() is provided, see below. sd_id128_get_machine_app_specific() is similar to diff --git a/man/sd_journal_get_data.xml b/man/sd_journal_get_data.xml index 0a0030e30..209f5deaa 100644 --- a/man/sd_journal_get_data.xml +++ b/man/sd_journal_get_data.xml @@ -18,6 +18,7 @@ sd_journal_get_data sd_journal_enumerate_data + sd_journal_enumerate_available_data sd_journal_restart_data SD_JOURNAL_FOREACH_DATA sd_journal_set_data_threshold @@ -44,6 +45,13 @@ size_t *length + + int sd_journal_enumerate_available_data + sd_journal *j + const void **data + size_t *length + + void sd_journal_restart_data sd_journal *j @@ -73,24 +81,18 @@ Description - sd_journal_get_data() gets the data - object associated with a specific field from the current journal - entry. It takes four arguments: the journal context object, a - string with the field name to request, plus a pair of pointers to - pointer/size variables where the data object and its size shall be - stored in. The field name should be an entry field name. - Well-known field names are listed in - systemd.journal-fields7. - The returned data is in a read-only memory map and is only valid - until the next invocation of - sd_journal_get_data() or - sd_journal_enumerate_data(), or the read - pointer is altered. Note that the data returned will be prefixed - with the field name and '='. Also note that, by default, data fields - larger than 64K might get truncated to 64K. This threshold may be - changed and turned off with - sd_journal_set_data_threshold() (see - below). + sd_journal_get_data() gets the data object associated with a specific field + from the current journal entry. It takes four arguments: the journal context object, a string with the + field name to request, plus a pair of pointers to pointer/size variables where the data object and its + size shall be stored in. The field name should be an entry field name. Well-known field names are listed in + systemd.journal-fields7, + but any field can be specified. The returned data is in a read-only memory map and is only valid until + the next invocation of sd_journal_get_data(), + sd_journal_enumerate_data(), + sd_journal_enumerate_available_data(), or when the read pointer is altered. Note + that the data returned will be prefixed with the field name and =. Also note that, by + default, data fields larger than 64K might get truncated to 64K. This threshold may be changed and turned + off with sd_journal_set_data_threshold() (see below). sd_journal_enumerate_data() may be used to iterate through all fields of the current entry. On each @@ -99,15 +101,18 @@ format as with sd_journal_get_data() and also follows the same life-time semantics. + sd_journal_enumerate_available_data() is similar to + sd_journal_enumerate_data(), but silently skips any fields which may be valid, but + are too large or not supported by current implementation. + sd_journal_restart_data() resets the data enumeration index to the beginning of the entry. The next invocation of sd_journal_enumerate_data() will return the first field of the entry again. - Note that the SD_JOURNAL_FOREACH_DATA() - macro may be used as a handy wrapper around - sd_journal_restart_data() and - sd_journal_enumerate_data(). + Note that the SD_JOURNAL_FOREACH_DATA() macro may be used as a handy wrapper + around sd_journal_restart_data() and + sd_journal_enumerate_available_data(). Note that these functions will not work before sd_journal_next3 @@ -139,18 +144,88 @@ Return Value - sd_journal_get_data() returns 0 on - success or a negative errno-style error code. If the current entry - does not include the specified field, -ENOENT is returned. If - sd_journal_next3 - has not been called at least once, -EADDRNOTAVAIL is returned. - sd_journal_enumerate_data() returns a - positive integer if the next field has been read, 0 when no more - fields are known, or a negative errno-style error code. - sd_journal_restart_data() returns nothing. - sd_journal_set_data_threshold() and - sd_journal_get_threshold() return 0 on - success or a negative errno-style error code. + sd_journal_get_data() returns 0 on success or a negative errno-style error + code. sd_journal_enumerate_data() and + sd_journal_enumerate_available_data() return a positive integer if the next field + has been read, 0 when no more fields remain, or a negative errno-style error code. + sd_journal_restart_data() doesn't return anything. + sd_journal_set_data_threshold() and sd_journal_get_threshold() + return 0 on success or a negative errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + One of the required parameters is NULL or invalid. + + + + + -ECHILD + + The journal object was created in a different process. + + + + -EADDRNOTAVAIL + + The read pointer is not positioned at a valid entry; + sd_journal_next3 + or a related call has not been called at least once. + + + + -ENOENT + + The current entry does not include the specified field. + + + + + -ENOMEM + + Memory allocation failed. + + + + -ENOBUFS + + A compressed entry is too large. + + + + -E2BIG + + The data field is too large for this computer architecture (e.g. above 4 GB on a + 32-bit architecture). + + + + -EPROTONOSUPPORT + + The journal is compressed with an unsupported method or the journal uses an + unsupported feature. + + + + -EBADMSG + + The journal is corrupted (possibly just the entry being iterated over). + + + + + -EIO + + An I/O error was reported by the kernel. + + + diff --git a/man/sd_journal_has_runtime_files.xml b/man/sd_journal_has_runtime_files.xml index 7e6e7d4b9..02fdc1247 100644 --- a/man/sd_journal_has_runtime_files.xml +++ b/man/sd_journal_has_runtime_files.xml @@ -21,7 +21,7 @@ sd_journal_has_runtime_files sd_journal_has_persistent_files - Query availability of runtime or persistent journal files. + Query availability of runtime or persistent journal files diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml index 9884beae1..bdece26cc 100644 --- a/man/sd_journal_open.xml +++ b/man/sd_journal_open.xml @@ -22,6 +22,7 @@ sd_journal_open_directory_fd sd_journal_open_files sd_journal_open_files_fd + sd_journal_open_namespace sd_journal_close sd_journal SD_JOURNAL_LOCAL_ONLY diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml index f6973087f..84adab5c7 100644 --- a/man/sd_journal_print.xml +++ b/man/sd_journal_print.xml @@ -22,6 +22,12 @@ sd_journal_sendv sd_journal_perror SD_JOURNAL_SUPPRESS_LOCATION + sd_journal_print_with_location + sd_journal_printv_with_location + sd_journal_send_with_location + sd_journal_sendv_with_location + sd_journal_perror_with_location + Submit log entries to the journal @@ -60,6 +66,51 @@ const char *message + + int sd_journal_print_with_location + const char *file + const char *line + const char *func + int priority + const char *format + + + + + int sd_journal_printv_with_location + int priority + const char *file + const char *line + const char *func + const char *format + va_list ap + + + + int sd_journal_send_with_location + const char *file + const char *line + const char *func + const char *format + + + + + int sd_journal_sendv_with_location + const char *file + const char *line + const char *func + const struct iovec *iov + int n + + + + int sd_journal_perror_with_location + const char *file + const char *line + const char *func + const char *message + @@ -136,11 +187,20 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid( "PRIORITY=%i", LOG_INFO, NULL); - Note that these calls implicitly add fields for the source - file, function name and code line where invoked. This is - implemented with macros. If this is not desired, it can be turned - off by defining SD_JOURNAL_SUPPRESS_LOCATION before including - sd-journal.h. + Note that these calls implicitly add fields for the source file, function name and code line where + invoked. This is implemented with macros. If this is not desired, it can be turned off by defining + SD_JOURNAL_SUPPRESS_LOCATION before including sd-journal.h. + + + sd_journal_print_with_location, + sd_journal_printv_with_location, sd_journal_send_with_location, + sd_journal_sendv_with_location, and + sd_journal_perror_with_location are similar to their counterparts without + _with_location, but accept additional parameters to explicitly set the source file + name, function, and line. Those arguments must contain valid journal entries including the variable name, + e.g. CODE_FILE=src/foo.c, CODE_LINE=666, + CODE_FUNC=myfunc. These variants are primarily useful when writing custom wrappers, + for example in bindings for a different language. syslog3 and sd_journal_print() may @@ -163,9 +223,9 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid( Return Value - The five calls return 0 on success or a negative errno-style error code. The errno3 variable itself is - not altered. + The ten functions return 0 on success or a negative errno-style error code. The + errno3 + variable itself is not altered. If systemd-journald8 @@ -178,15 +238,17 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid( - sd_journal_sendv() is "async signal safe" in the meaning of signal-safety7. + sd_journal_sendv() and sd_journal_sendv_with_location() + are "async signal safe" in the meaning of + signal-safety7. sd_journal_print, sd_journal_printv, - sd_journal_send, and - sd_journal_perror are - not async signal safe. + sd_journal_send, + sd_journal_perror, + and their counterparts with _with_location + are not async signal safe. diff --git a/man/sd_journal_query_unique.xml b/man/sd_journal_query_unique.xml index 1bf83968d..88beaa646 100644 --- a/man/sd_journal_query_unique.xml +++ b/man/sd_journal_query_unique.xml @@ -18,6 +18,7 @@ sd_journal_query_unique sd_journal_enumerate_unique + sd_journal_enumerate_available_unique sd_journal_restart_unique SD_JOURNAL_FOREACH_UNIQUE Read unique data fields from the journal @@ -33,6 +34,13 @@ const char *field + + int sd_journal_enumerate_available_unique + sd_journal *j + const void **data + size_t *length + + int sd_journal_enumerate_unique sd_journal *j @@ -58,33 +66,31 @@ Description - sd_journal_query_unique() queries the - journal for all unique values the specified field can take. It - takes two arguments: the journal to query and the field name to - look for. Well-known field names are listed on - systemd.journal-fields7. - Field names must be specified without a trailing '='. After this - function has been executed successfully the field values may be - queried using sd_journal_enumerate_unique(). - Invoking this call a second time will change the field name being - queried and reset the enumeration index to the first field value - that matches. + sd_journal_query_unique() queries the journal for all unique values the + specified field can take. It takes two arguments: the journal to query and the field name to look + for. Well-known field names are listed on + systemd.journal-fields7, + but any field can be specified. Field names must be specified without a trailing + =. After this function has been executed successfully the field values may be queried + using sd_journal_enumerate_unique() and + sd_journal_enumerate_available_unique(). Invoking one of those calls will change the + field name being queried and reset the enumeration index to the first field value that matches. - sd_journal_enumerate_unique() may be - used to iterate through all data fields which match the previously - selected field name as set with - sd_journal_query_unique(). On each invocation - the next field data matching the field name is returned. The order - of the returned data fields is not defined. It takes three - arguments: the journal context object, plus a pair of pointers to - pointer/size variables where the data object and its size shall be - stored in. The returned data is in a read-only memory map and is - only valid until the next invocation of - sd_journal_enumerate_unique(). Note that the - data returned will be prefixed with the field name and '='. Note - that this call is subject to the data field size threshold as - controlled by - sd_journal_set_data_threshold(). + sd_journal_enumerate_unique() may be used to iterate through all data fields + which match the previously selected field name as set with + sd_journal_query_unique(). On each invocation the next field data matching the field + name is returned. The order of the returned data fields is not defined. It takes three arguments: the + journal object, plus a pair of pointers to pointer/size variables where the data object and its size + shall be stored. The returned data is in a read-only memory map and is only valid until the next + invocation of sd_journal_enumerate_unique(). Note that the data returned will be + prefixed with the field name and =. Note that this call is subject to the data field + size threshold as controlled by sd_journal_set_data_threshold() and only the initial + part of the field up to the threshold is returned. An error is returned for fields which cannot be + retrieved. See the error list below for details. + + sd_journal_enumerate_available_unique() is similar to + sd_journal_enumerate_unique(), but silently skips any fields which may be valid, but + are too large or not supported by current implementation. sd_journal_restart_unique() resets the data enumeration index to the beginning of the list. The next @@ -92,11 +98,9 @@ will return the first field data matching the field name again. - Note that the - SD_JOURNAL_FOREACH_UNIQUE() macro may be used - as a handy wrapper around - sd_journal_restart_unique() and - sd_journal_enumerate_unique(). + Note that the SD_JOURNAL_FOREACH_UNIQUE() macro may be used as a handy wrapper + around sd_journal_restart_unique() and + sd_journal_enumerate_available_unique(). Note that these functions currently are not influenced by matches set with sd_journal_add_match() but @@ -111,13 +115,29 @@ Return Value - sd_journal_query_unique() returns 0 on - success or a negative errno-style error code. - sd_journal_enumerate_unique() returns a - positive integer if the next field data has been read, 0 when no - more fields are known, or a negative errno-style error code. - sd_journal_restart_unique() returns - nothing. + sd_journal_query_unique() returns 0 on success or a negative errno-style error + code. sd_journal_enumerate_unique() and and + sd_journal_query_available_unique() return a positive integer if the next field data + has been read, 0 when no more fields remain, or a negative errno-style error code. + sd_journal_restart_unique() doesn't return anything. + + + Errors + + Returned errors may indicate the following problems: + + + + + + + + + + + + + @@ -131,10 +151,9 @@ Examples - Use the SD_JOURNAL_FOREACH_UNIQUE macro - to iterate through all values a field of the journal can take. The - following example lists all unit names referenced in the - journal: + Use the SD_JOURNAL_FOREACH_UNIQUE macro to iterate through all values a field + of the journal can take (and which can be accessed on the given architecture and are not compressed with + an unsupported mechanism). The following example lists all unit names referenced in the journal: diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml index be1c843ee..d1c83e2d2 100644 --- a/man/sd_login_monitor_new.xml +++ b/man/sd_login_monitor_new.xml @@ -112,7 +112,7 @@ code block is left: { - __attribute__((cleanup(sd_login_monitor_unrefp)) sd_login_monitor *m = NULL; + __attribute__((cleanup(sd_login_monitor_unrefp))) sd_login_monitor *m = NULL; int r; … r = sd_login_monitor_default(&m); diff --git a/man/sd_machine_get_class.xml b/man/sd_machine_get_class.xml index db6cf0dbc..cd259c863 100644 --- a/man/sd_machine_get_class.xml +++ b/man/sd_machine_get_class.xml @@ -19,7 +19,7 @@ sd_machine_get_class sd_machine_get_ifindices Determine the class and network interface indices of a - locally running virtual machine or container. + locally running virtual machine or container diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 3046ca88e..87b12c4bd 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -22,6 +22,7 @@ sd_pid_notify sd_pid_notifyf sd_pid_notify_with_fds + sd_notify_barrier Notify service manager about start-up completion and other service status changes @@ -65,6 +66,12 @@ const int *fds unsigned n_fds + + + int sd_notify_barrier + int unset_environment + uint64_t timeout + @@ -219,8 +226,8 @@ in a memfd_create2 memory file descriptor. Note that the service manager will accept messages for a service only if its FileDescriptorStoreMax= setting is non-zero (defaults to zero, see - systemd.service5). If file - descriptors sent are pollable (see + systemd.service5). If + FDPOLL=0 is not set and the file descriptors sent are pollable (see epoll_ctl2), then any EPOLLHUP or EPOLLERR event seen on them will result in their automatic removal from the store. Multiple arrays of file descriptors may be sent in separate messages, in @@ -251,6 +258,27 @@ submitted name does not follow these restrictions, it is ignored. + + FDPOLL=0 + + When used in combination with FDSTORE=1, disables polling of the stored + file descriptors regardless of whether or not they are pollable. As this option disables automatic cleanup + of the stored file descriptors on EPOLLERR and EPOLLHUP, care must be taken to ensure proper manual cleanup. + Use of this option is not generally recommended except for when automatic cleanup has unwanted behavior such + as prematurely discarding file descriptors from the store. + + + + BARRIER=1 + + Tells the service manager that the client is explicitly requesting synchronization by means of + closing the file descriptor sent with this command. The service manager guarantees that the processing of a + BARRIER=1 command will only happen after all previous notification messages sent before this command + have been processed. Hence, this command accompanied with a single file descriptor can be used to synchronize + against reception of all previous status messages. Note that this command cannot be mixed with other notifications, + and has to be sent in a separate message to the service manager, otherwise all assignments will be ignored. Note that + sending 0 or more than 1 file descriptor with this command is a violation of the protocol. + It is recommended to prefix variable names that are not @@ -272,6 +300,13 @@ attribute the message to the unit, and thus will ignore it, even if NotifyAccess= is set for it. + Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications + to units correctly, sd_notify_barrier() may be used. This call acts as a synchronization point + and ensures all notifications sent before this call have been picked up by the service manager when it returns + successfully. Use of sd_notify_barrier() is needed for clients which are not invoked by the + service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the + unit. + sd_notifyf() is similar to sd_notify() but takes a printf()-like format string plus @@ -302,6 +337,14 @@ to the service manager on messages that do not expect them (i.e. without FDSTORE=1) they are immediately closed on reception. + + sd_notify_barrier() allows the caller to + synchronize against reception of previously sent notification messages + and uses the BARRIER=1 command. It takes a relative + timeout value in microseconds which is passed to + ppoll2 + . A value of UINT64_MAX is interpreted as infinite timeout. + @@ -392,6 +435,22 @@ sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &fd, 1); + + + Eliminating race conditions + + When the client sending the notifications is not spawned + by the service manager, it may exit too quickly and the service + manager may fail to attribute them correctly to the unit. To + prevent such races, use sd_notify_barrier() + to synchronize against reception of all notifications sent before + this call is made. + + sd_notify(0, "READY=1"); + /* set timeout to 5 seconds */ + sd_notify_barrier(0, 5 * 1000000); + + diff --git a/man/sd_path_lookup.xml b/man/sd_path_lookup.xml new file mode 100644 index 000000000..9316090cc --- /dev/null +++ b/man/sd_path_lookup.xml @@ -0,0 +1,215 @@ + + + + + + + + sd_path_lookup + systemd + + + + sd_path_lookup + 3 + + + + sd_path_lookup + sd_path_lookup_strv + + Query well-known file system paths + + + + + #include <systemd/sd-path.h> + + + + enum { + SD_PATH_TEMPORARY, + SD_PATH_TEMPORARY_LARGE, + + SD_PATH_SYSTEM_BINARIES, + SD_PATH_SYSTEM_INCLUDE, + SD_PATH_SYSTEM_LIBRARY_PRIVATE, + SD_PATH_SYSTEM_LIBRARY_ARCH, + SD_PATH_SYSTEM_SHARED, + SD_PATH_SYSTEM_CONFIGURATION_FACTORY, + SD_PATH_SYSTEM_STATE_FACTORY, + + SD_PATH_SYSTEM_CONFIGURATION, + SD_PATH_SYSTEM_RUNTIME, + SD_PATH_SYSTEM_RUNTIME_LOGS, + SD_PATH_SYSTEM_STATE_PRIVATE, + SD_PATH_SYSTEM_STATE_LOGS, + SD_PATH_SYSTEM_STATE_CACHE, + SD_PATH_SYSTEM_STATE_SPOOL, + + SD_PATH_USER_BINARIES, + SD_PATH_USER_LIBRARY_PRIVATE, + SD_PATH_USER_LIBRARY_ARCH, + SD_PATH_USER_SHARED, + + SD_PATH_USER_CONFIGURATION, + SD_PATH_USER_RUNTIME, + SD_PATH_USER_STATE_CACHE, + + SD_PATH_USER, + SD_PATH_USER_DOCUMENTS, + SD_PATH_USER_MUSIC, + SD_PATH_USER_PICTURES, + SD_PATH_USER_VIDEOS, + SD_PATH_USER_DOWNLOAD, + SD_PATH_USER_PUBLIC, + SD_PATH_USER_TEMPLATES, + SD_PATH_USER_DESKTOP, + + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_BINARIES_DEFAULT, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION, + + SD_PATH_SYSTEMD_UTIL, + SD_PATH_SYSTEMD_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SYSTEM_PRESET, + SD_PATH_SYSTEMD_USER_UNIT, + SD_PATH_SYSTEMD_USER_PRESET, + SD_PATH_SYSTEMD_SYSTEM_CONF, + SD_PATH_SYSTEMD_USER_CONF, + SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SEARCH_USER_UNIT, + SD_PATH_SYSTEMD_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_USER_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR, + SD_PATH_SYSTEMD_SLEEP, + SD_PATH_SYSTEMD_SHUTDOWN, + + SD_PATH_TMPFILES, + SD_PATH_SYSUSERS, + SD_PATH_SYSCTL, + SD_PATH_BINFMT, + SD_PATH_MODULES_LOAD, + SD_PATH_CATALOG, + + SD_PATH_SYSTEMD_SEARCH_NETWORK, +}; + + + int sd_path_lookup + uint64_t type + const char *suffix + char **paths + + + + int sd_path_lookup_strv + uint64_t type + const char *suffix + char ***paths + + + + + + Description + + sd_path_lookup() and sd_bus_path_lookup_strv() return a + single path or set of file system paths specified by the argument type. In case of + sd_path_lookup() a single NUL-terminated string is returned. + When type specifies a set of paths, they are concatenated using + : as a separator (as is traditionally done for e.g. $PATH or + $LD_LIBRARY_PATH). In case of sd_path_lookup_strv() a + NULL-terminated array of strings is returned (strv). If suffix + suffix is given, it is concatenated to each of the paths after a slash + (/). All returned paths are absolute. + + For paths which refer to user directories, the relevant XDG standard is followed, with support for + environment variables like $XDG_DOCUMENTS_DIR, $XDG_DESKTOP_DIR, + ..., and explicit configuration in /etc/xdg/user-dirs.conf or + ${XDG_CONFIG_HOME}/user-dirs.dirs. See + + XDG Base Directory Specification for details. + + systemd-path1 is + a wrapper around sd_path_lookup() and allows the same set of paths to be queried. + + + + + Return Value + + On success, sd_path_lookup() and sd_path_lookup_strv() + return a non-negative integer. On failure, a negative errno-style error number is returned by either + function. + + The returned string or string array (strv) must be + free3'd by the + caller. + + + Errors + + Returned errors may indicate the following problems: + + + + -EOPNOTSUPP + + Unknown identifier type. + + + + -EINVAL + + Output argument is NULL. + + + + -ENXIO + + Query failed because of an undefined environment variable (e.g. for + SD_PATH_USER_RUNTIME when $XDG_RUNTIME_DIR is not + defined). + + + + -ENOMEM + + Memory allocation failed. + + + + + + + Examples + + + Look up the location of ~/Documents + + + Note that the default answer of $HOME/Documents may be + overridden by user-dirs.conf or + $XDG_DOCUMENTS_DIR. + + + + + + + See Also + + + systemd-path1 + + + + diff --git a/man/sd_pid_get_owner_uid.xml b/man/sd_pid_get_owner_uid.xml index e9d7a8eb6..9c16d5bc9 100644 --- a/man/sd_pid_get_owner_uid.xml +++ b/man/sd_pid_get_owner_uid.xml @@ -35,7 +35,7 @@ sd_peer_get_cgroup Determine the owner uid of the user unit or session, or the session, user unit, system unit, container/VM or slice that - a specific PID or socket peer belongs to. + a specific PID or socket peer belongs to diff --git a/man/sd_seat_get_active.xml b/man/sd_seat_get_active.xml index 2dba6803f..cf70b3578 100644 --- a/man/sd_seat_get_active.xml +++ b/man/sd_seat_get_active.xml @@ -19,7 +19,6 @@ sd_seat_get_active sd_seat_get_sessions - sd_seat_can_multi_session sd_seat_can_tty sd_seat_can_graphical Determine state of a specific seat @@ -44,11 +43,6 @@ unsigned int *n_uids - - int sd_seat_can_multi_session - const char *seat - - int sd_seat_can_tty const char *seat @@ -90,11 +84,6 @@ NULL may be returned and should be considered equivalent to an empty array. - sd_seat_can_multi_session() may be used - to determine whether a specific seat is capable of multi-session, - i.e. allows multiple login sessions in parallel (with only one - being active at a time). - sd_seat_can_tty() may be used to determine whether a specific seat provides TTY functionality, i.e. is useful as a text console. @@ -114,7 +103,7 @@ On success, sd_seat_get_active() returns 0 or a positive integer. On success, sd_seat_get_sessions() returns the number of entries in the session identifier - array. If the test succeeds, sd_seat_can_multi_session, + array. If the test succeeds, sd_seat_can_tty and sd_seat_can_graphical return a positive integer, if it fails 0. On failure, these calls return a negative errno-style error code. @@ -157,6 +146,14 @@ + + History + + In the past, sd_seat_can_multi_session() was used to check whether the seat + supports multiple sessions. All seats support that now, so that function has been deprecated and always + returns true. + + See Also diff --git a/man/standard-conf.xml b/man/standard-conf.xml index a58c76d85..ee8cc7bc0 100644 --- a/man/standard-conf.xml +++ b/man/standard-conf.xml @@ -13,7 +13,7 @@ Configuration files are read from directories in /etc/, /run/, /usr/local/lib/, and /usr/lib/, in - order of precedence, as listed in the SYNOPSIS section above. Files must have the the + order of precedence, as listed in the SYNOPSIS section above. Files must have the .conf extension. Files in /etc/ override files with the same name in /run/, /usr/local/lib/, and /usr/lib/. Files in /run/ override files with the same name diff --git a/man/standard-specifiers.xml b/man/standard-specifiers.xml new file mode 100644 index 000000000..3efbb6db0 --- /dev/null +++ b/man/standard-specifiers.xml @@ -0,0 +1,62 @@ + + + + + + + %b + Boot ID + The boot ID of the running system, formatted as string. See random4 for more information. + + + %a + Architecture + A short string identifying the architecture of the local system. A string such as x86, x86-64 or arm64. See the architectures defined for ConditionArchitecture= in systemd.unit5 for a full list. + + + %B + Operating system build ID + The operating system build identifier of the running system, as read from the BUILD_ID= field of /etc/os-release. If not set, resolves to an empty string. See os-release5 for more information. + + + %H + Host name + The hostname of the running system. + + + %l + Short host name + The hostname of the running system, truncated at the first dot to remove any domain component. + + + %m + Machine ID + The machine ID of the running system, formatted as string. See machine-id5 for more information. + + + %o + Operating system ID + The operating system identifier of the running system, as read from the ID= field of /etc/os-release. See os-release5 for more information. + + + %v + Kernel release + Identical to uname -r output. + + + %w + Operating system version ID + The operating system version identifier of the running system, as read from the VERSION_ID= field of /etc/os-release. If not set, resolves to an empty string. See os-release5 for more information. + + + %W + Operating system variant ID + The operating system variant identifier of the running system, as read from the VARIANT_ID= field of /etc/os-release. If not set, resolves to an empty string. See os-release5 for more information. + + + %% + Single percent sign + Use %% in place of % to specify a single percent sign. + + diff --git a/man/sysctl.d.xml b/man/sysctl.d.xml index f6713d398..70504510f 100644 --- a/man/sysctl.d.xml +++ b/man/sysctl.d.xml @@ -32,7 +32,7 @@ key.middle/part/with/dots.foo = 123 -key.that.will.not.fail = value key.pattern.*.with.glob = whatever -key.pattern.excluded.with.glob -key.pattern.overriden.with.glob = custom +key.pattern.overridden.with.glob = custom @@ -63,18 +63,18 @@ key.pattern.overriden.with.glob = custom net.ipv4.conf.enp3s0/200.forwarding or net/ipv4/conf/enp3s0.200/forwarding may be used to refer to /proc/sys/net/ipv4/conf/enp3s0.200/forwarding. A glob - glob7 pattern may be + glob7 pattern may be used to write the same value to all matching keys. Keys for which an explicit pattern exists will be excluded from any glob matching. In addition, a key may be explicitly excluded from being set by any matching glob patterns by specifying the key name prefixed with a - character and not followed by =, see SYNOPSIS. Any access permission errors and attempts to write variables not present on the local system are - logged, but do not cause the service to fail. Debug log level is used, which means that the message will - not show up at all by default. Moreover, if a variable assignment is prefixed with a single - - character, any failure to set the variable will be logged at debug level, but will - not cause the service to fail. All other errors when setting variables are logged with higher priority - and cause the service to return failure at the end (other variables are still processed). + logged at debug level and do not cause the service to fail. Moreover, if a variable assignment is + prefixed with a single - character, failure to set the variable for other reasons will + be logged at debug level and will not cause the service to fail. In other cases, errors when setting + variables are logged with higher priority and cause the service to return failure at the end (after + processing other variables). The settings configured with sysctl.d files will be applied early on boot. The network interface-specific options will also be applied individually for each network interface as it diff --git a/man/systemctl.xml b/man/systemctl.xml index 3f3615156..506f9ca68 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -301,6 +301,30 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago generally redundant and reproducible on the next invocation of the unit). + + freeze PATTERN + + + Freeze one or more units specified on the + command line using cgroup freezer + + Freezing the unit will cause all processes contained within the cgroup corresponding to the unit + to be suspended. Being suspended means that unit's processes won't be scheduled to run on CPU until thawed. + Note that this command is supported only on systems that use unified cgroup hierarchy. Unit is automatically + thawed just before we execute a job against the unit, e.g. before the unit is stopped. + + + + thaw PATTERN + + + Thaw (unfreeze) one or more units specified on the + command line. + + This is the inverse operation to the freeze command and resumes the execution of + processes in the unit's cgroup. + + is-active PATTERN @@ -386,7 +410,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err The "Loaded:" line in the output will show loaded if the unit has been loaded into memory. Other possible values for "Loaded:" include: error if there was a problem - loading it, not-found if not unit file was found for this unit, + loading it, not-found if no unit file was found for this unit, bad-setting if an essential unit file setting could not be parsed and masked if the unit file has been masked. Along with showing the path to the unit file, this line will also show the enablement state. Enabled commands start at boot. See the full table of @@ -556,7 +580,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Enable one or more units or unit instances. This will create a set of symlinks, as encoded in the - [Install] sections of the indicated unit files. After the symlinks have been created, + [Install] sections of the indicated unit files. After the symlinks have been created, the system manager configuration is reloaded (in a way equivalent to daemon-reload), in order to ensure the changes are taken into account immediately. Note that this does not have the effect of also starting any of the units being enabled. If this is @@ -579,7 +603,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err . - Note that this operation creates only the symlinks suggested in the [Install] + Note that this operation creates only the symlinks suggested in the [Install] section of the unit files. While this command is the recommended way to manipulate the unit configuration directory, the administrator is free to make additional changes manually by placing or removing symlinks below this directory. This is particularly useful to create configurations that deviate from the suggested @@ -619,7 +643,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err This command expects valid unit names only, it does not accept paths to unit files. In addition to the units specified as arguments, all units are disabled that are listed in the - Also= setting contained in the [Install] section of any of the unit + Also= setting contained in the [Install] section of any of the unit files being operated on. This command implicitly reloads the system manager configuration after completing the operation. Note @@ -642,7 +666,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Reenable one or more units, as specified on the command line. This is a combination of disable and enable and is useful to reset the symlinks a unit file is - enabled with to the defaults configured in its [Install] section. This command expects + enabled with to the defaults configured in its [Install] section. This command expects a unit name only, it does not accept paths to unit files. @@ -727,6 +751,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err linked-runtime + + alias + The name is an alias (symlink to another unit file). + 0 + masked Completely disabled, so that any start operation on it fails (permanently in /etc/systemd/system/ or transiently in /run/systemd/systemd/). @@ -737,17 +766,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err static - The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section. + The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section. 0 indirect - The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in Also=. For template unit file, an instance different than the one specified in DefaultInstance= is enabled. + The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in Also=. For template unit files, an instance different than the one specified in DefaultInstance= is enabled. 0 disabled - The unit file is not enabled, but contains an [Install] section with installation instructions. + The unit file is not enabled, but contains an [Install] section with installation instructions. > 0 @@ -1065,7 +1094,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - + log-level [LEVEL] If no argument is given, print the current log level of the manager. If an @@ -1264,7 +1293,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - reboot arg + reboot Shut down and reboot the system. This is mostly equivalent to systemctl start reboot.target @@ -1280,11 +1309,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err systemctl itself, and the system manager is not contacted. This means the command should succeed even when the system manager has crashed. - If the optional argument arg is given, it will be passed as the optional + If the switch is given, it will be passed as the optional argument to the reboot2 - system call. The value is architecture and firmware specific. As an example, recovery - might be used to trigger system recovery, and fota might be used to trigger a - firmware over the air update. + system call. @@ -1500,6 +1527,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + + Equivalent to , i.e. shows the + value of the property without the property name or =. Note that using + once will also affect all properties listed with + /. + + + @@ -1625,9 +1663,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When printing properties with show, - only print the value, and skip the property name and - =. + When printing properties with show, only print the value, and skip the + property name and =. Also see option above. @@ -2059,8 +2096,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err When used with the reboot command, indicate to the system's boot loader to show the - boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass - zero in order to disable the menu time-out. Note that not all boot loaders support this + boot loader menu on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass + zero in order to disable the menu timeout. Note that not all boot loaders support this functionality. @@ -2076,6 +2113,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + + This switch is used with reboot. The value is architecture and firmware specific. As an example, recovery + might be used to trigger system recovery, and fota might be used to trigger a + firmware over the air update. + + + diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 1106c36d0..3d6de1710 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -509,9 +509,9 @@ NAutoVTs=8 This command will load unit files and print warnings if any errors are detected. Files specified on the command line will be loaded, but also any other units referenced by them. The full unit search path is formed by combining the directories for all command line arguments, and the usual unit load - paths (variable $SYSTEMD_UNIT_PATH is supported, and may be used to replace or + paths. The variable $SYSTEMD_UNIT_PATH is supported, and may be used to replace or augment the compiled in set of unit load paths; see - systemd.unit5). All + systemd.unit5. All units files present in the directories containing the command line arguments will be used in preference to the other paths. @@ -669,7 +669,7 @@ Service b@0.service not loaded, b.socket cannot be started. dot command (see above), this selects which relationships are shown in the dependency graph. Both options require a - glob7 + glob7 pattern as an argument, which will be matched against the left-hand and the right-hand, respectively, nodes of a relationship. @@ -700,9 +700,9 @@ Service b@0.service not loaded, b.socket cannot be started. - Do not invoke man to verify the existence of - man pages listed in Documentation=. - + Do not invoke + man1 + to verify the existence of man pages listed in Documentation=. diff --git a/man/systemd-bless-boot-generator.xml b/man/systemd-bless-boot-generator.xml index 0c5144f6b..8275838bc 100644 --- a/man/systemd-bless-boot-generator.xml +++ b/man/systemd-bless-boot-generator.xml @@ -17,7 +17,7 @@ systemd-bless-boot-generator - Pull systemd-bless-boot.service into the initial boot transaction when boot counting is in effect. + Pull systemd-bless-boot.service into the initial boot transaction when boot counting is in effect diff --git a/man/systemd-bless-boot.service.xml b/man/systemd-bless-boot.service.xml index 4a45560a8..1787d7216 100644 --- a/man/systemd-bless-boot.service.xml +++ b/man/systemd-bless-boot.service.xml @@ -106,7 +106,7 @@ systemd1, systemd-boot7, - systemd.special1 + systemd.special7 diff --git a/man/systemd-boot-check-no-failures.service.xml b/man/systemd-boot-check-no-failures.service.xml index 8509fa633..e92fdd63c 100644 --- a/man/systemd-boot-check-no-failures.service.xml +++ b/man/systemd-boot-check-no-failures.service.xml @@ -45,7 +45,7 @@ See Also systemd1, - systemd.special1 + systemd.special7 diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index ee513ed40..ee6ad1d53 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -91,7 +91,7 @@ The boot manager optionally reads a random seed from the ESP partition, combines it with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as - entropy pool initializaton, providing a full entropy pool during early boot. + entropy pool initialization, providing a full entropy pool during early boot. bootctl1 @@ -123,6 +123,7 @@ (Enter) + (Right) Boot selected entry @@ -166,6 +167,7 @@ h ? + F1 Show a help screen @@ -312,7 +314,7 @@ is maintained persistently, while LoaderConfigTimeoutOneShot is a one-time override which is read once (in which case it takes precedence over LoaderConfigTimeout) and then removed. LoaderConfigTimeout may be manipulated with the - t/T keys, see above.) + t/T keys, see above. @@ -342,7 +344,7 @@ boots. bootctl1's and commands make use of these variables. The boot loader modifies LoaderEntryDefault on request, when the d key is used, see - above.) + above. @@ -421,9 +423,9 @@ LoaderSystemToken - A binary random data field, that is used for generating the random see to pass to the - OS (see above). Note that this random data is generally only generated once, during OS installation, - and is then never updated again. + A binary random data field, that is used for generating the random seed to pass to + the OS (see above). Note that this random data is generally only generated once, during OS + installation, and is then never updated again. @@ -475,10 +477,10 @@ considered 'good' from then on. The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad' - state are ordered at the end of the list, and entries in 'good' or 'indeterminate' at the beginning. The user can + state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is - automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the top item of - the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or + automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom + item of the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or 'indeterminate' entries left. The kernel-install8 kernel diff --git a/man/systemd-cryptsetup@.service.xml b/man/systemd-cryptsetup@.service.xml index 0324a6744..47051b9ce 100644 --- a/man/systemd-cryptsetup@.service.xml +++ b/man/systemd-cryptsetup@.service.xml @@ -44,6 +44,32 @@ At early boot and when the system manager configuration is reloaded, /etc/crypttab is translated into systemd-cryptsetup@.service units by systemd-cryptsetup-generator8. + + In order to unlock a volume a password or binary key is + required. systemd-cryptsetup@.service tries to acquire a suitable password or binary + key via the following mechanisms, tried in order: + + + If a key file is explicitly configured (via the third column in + /etc/crypttab), a key read from it is used. If a PKCS#11 token is configured + (using the pkcs11-uri= option) the key is decrypted before use. + + If no key file is configured explicitly this way, a key file is automatically loaded + from /etc/cryptsetup-keys.d/volume.key and + /run/cryptsetup-keys.d/volume.key, if present. Here + too, if a PKCS#11 token is configured, any key found this way is decrypted before + use. + + If the try-empty-password option is specified it is then attempted + to unlock the volume with an empty password. + + The kernel keyring is then checked for a suitable cached password from previous + attempts. + + Finally, the user is queried for a password, possibly multiple times. + + + If no suitable key may be acquired via any of the mechanisms describes above, volume activation fails. diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index d599ac20f..77bdd80f3 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -167,6 +167,11 @@ wsl Windows Subsystem for Linux + + + proot + proot userspace chroot/bind mount emulation + diff --git a/man/systemd-environment-d-generator.xml b/man/systemd-environment-d-generator.xml index 4c88bd5e6..708db01b4 100644 --- a/man/systemd-environment-d-generator.xml +++ b/man/systemd-environment-d-generator.xml @@ -46,7 +46,7 @@ systemd1, systemctl1, systemd.environment-generator7, - systemd.generator5 + systemd.generator7 diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index 560649f22..0976394b6 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -54,7 +54,7 @@ The system time zone - The system host name + The system hostname The machine ID of the system @@ -98,6 +98,18 @@ + + + Takes a path to a disk image file or block device node. If specified all operations + are applied to file system in the indicated disk image. This is similar to + but operates on file systems stored in disk images or block devices. The disk image should either + contain just a file system or a set of file systems within a GPT partition table, following the + Discoverable Partitions + Specification. For further information on supported disk images, see + systemd-nspawn1's + switch of the same name. + + @@ -133,7 +145,7 @@ Sets the system hostname. The argument should - be a host name, compatible with DNS. This controls the + be a hostname, compatible with DNS. This controls the hostname5 configuration file. @@ -150,18 +162,36 @@ + - Sets the password of the system's root user. - This creates a + Sets the password of the system's root user. This creates/modifies the + passwd5 and shadow5 - file. This setting exists in two forms: - accepts the password to set - directly on the command line, and - reads it from a file. - Note that it is not recommended to specify passwords on the - command line, as other users might be able to see them simply - by invoking - ps1. + files. This setting exists in three forms: accepts the password to + set directly on the command line, reads it from a file and + accepts an already hashed password on the command line. See + shadow5 + for more information on the format of the hashed password. Note that it is not recommended to specify + plaintext passwords on the command line, as other users might be able to see them simply by invoking + ps1. + + + + + + + Sets the shell of the system's root user. This creates/modifies the + passwd5 + file. + + + + + + Sets the system's kernel command line. This controls the + /etc/kernel/cmdline file which is used by + kernel-install8. + @@ -170,6 +200,7 @@ + Prompt the user interactively for a specific basic setting. Note that any explicit configuration settings @@ -186,7 +217,8 @@ , , , - in combination. + , + in combination. @@ -195,6 +227,7 @@ + Copy a specific basic setting from the host. This only works in combination with @@ -209,7 +242,8 @@ , , , - in combination. + , + in combination. @@ -221,6 +255,31 @@ . + + + + systemd-firstboot doesn't modify existing files unless + is specified. For modifications to /etc/passwd and + /etc/shadow, systemd-firstboot only modifies the entry of the + root user instead of overwriting the entire file. + + + + + + Removes the password of the system's root user, enabling login as root without a + password unless the root account is locked. Note that this is extremely insecure and hence this + option should not be used lightly. + + + + + + Takes a boolean argument. By default when prompting the user for configuration + options a brief welcome text is shown before the first question is asked. Pass false to this option + to turn off the welcome text. + + diff --git a/man/systemd-fstab-generator.xml b/man/systemd-fstab-generator.xml index ab3325885..59f189686 100644 --- a/man/systemd-fstab-generator.xml +++ b/man/systemd-fstab-generator.xml @@ -198,6 +198,15 @@ automatically populate /etc, and also /var in case of systemd.volatile=yes. + + + systemd.swap + + Takes a boolean argument or enables the option if specified + without an argument. If disabled, causes the generator to ignore + any swap devices configured in /etc/fstab. + Defaults to enabled. + diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index b0fa617d6..78fdaccca 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -19,7 +19,7 @@ systemd-gpt-auto-generator Generator for automatically discovering and mounting root, /home/, /srv/, /var/ and /var/tmp/ partitions, as - well as discovering and enabling swap partitions, based on GPT partition type GUIDs. + well as discovering and enabling swap partitions, based on GPT partition type GUIDs diff --git a/man/systemd-homed.service.xml b/man/systemd-homed.service.xml index f79c2be2d..ab8bcab8c 100644 --- a/man/systemd-homed.service.xml +++ b/man/systemd-homed.service.xml @@ -47,12 +47,64 @@ userdbctl1. + + Key Management + + User records are cryptographically signed with a public/private key pair (the signature is part of + the JSON record itself). For a user to be permitted to log in locally the public key matching the + signature of their user record must be installed. For a user record to be modified locally the private + key matching the signature must be installed locally, too. The keys are stored in the + /var/lib/systemd/home/ directory: + + + + + /var/lib/systemd/home/local.private + + The private key of the public/private key pair used for local records. Currently, + only a single such key may be installed. + + + + /var/lib/systemd/home/local.public + + The public key of the public/private key pair used for local records. Currently, + only a single such key may be installed. + + + + /var/lib/systemd/home/*.public + + Additional public keys. Any users whose user records are signed with any of these keys + are permitted to log in locally. An arbitrary number of keys may be installed this + way. + + + + All key files listed above are in PEM format. + + In order to migrate a home directory from a host foobar to another host + quux it is hence sufficient to copy + /var/lib/systemd/home/local.public from the host foobar to + quux, maybe calling the file on the destination + /var/lib/systemd/home/foobar.public, reflecting the origin of the key. If the user + record should be modifiable on quux the pair + /var/lib/systemd/home/local.public and + /var/lib/systemd/home/local.private need to be copied from foobar + to quux, and placed under the identical paths there, as currently only a single + private key is supported per host. Note of course that the latter means that user records + generated/signed before the key pair is copied in, lose their validity. + + See Also systemd1, + homed.conf5, homectl1, - userdbctl1 + pam_systemd_home8, + userdbctl1, + org.freedesktop.home15 diff --git a/man/systemd-hostnamed.service.xml b/man/systemd-hostnamed.service.xml index 19bd4c06f..1aa32a61c 100644 --- a/man/systemd-hostnamed.service.xml +++ b/man/systemd-hostnamed.service.xml @@ -18,7 +18,7 @@ systemd-hostnamed.service systemd-hostnamed - Host name bus mechanism + Daemon to control system hostname from programs @@ -29,19 +29,37 @@ Description - systemd-hostnamed is a system service - that may be used as a mechanism to change the system's hostname. - systemd-hostnamed is automatically activated - on request and terminates itself when it is unused. + systemd-hostnamed.service is a system service that may be used to change the + system's hostname and related machine metadata from user programs. It is automatically activated on + request and terminates itself when unused. + + It currently offers access to five variables: + + The current hostname (Example: dhcp-192-168-47-11) + + + The static (configured) hostname (Example: + lennarts-computer) + + The pretty hostname (Example: Lennart's Computer) + + + A suitable icon name for the local host (Example: + computer-laptop) + + A chassis type (Example: tablet) + + The tool hostnamectl1 is a command line client to this service. - See the - developer documentation for information about the APIs - systemd-hostnamed provides. + See + org.freedesktop.hostname15 + and + org.freedesktop.LogControl15 + for a description of the D-Bus API. diff --git a/man/systemd-importd.service.xml b/man/systemd-importd.service.xml index 355e5b0b9..fa3c3a812 100644 --- a/man/systemd-importd.service.xml +++ b/man/systemd-importd.service.xml @@ -36,10 +36,11 @@ pull-raw, pull-tar, import-raw, import-tar, export-raw, and export-tar commands. - See the - - importd D-Bus API Documentation for information about the - APIs systemd-importd provides. + See + org.freedesktop.import15 + and + org.freedesktop.LogControl15 + for a description of the D-Bus API. diff --git a/man/systemd-initctl.service.xml b/man/systemd-initctl.service.xml index 0345936fc..ea93efd3f 100644 --- a/man/systemd-initctl.service.xml +++ b/man/systemd-initctl.service.xml @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - + systemd-initctl.service diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml index 633b4cb55..a7c50f382 100644 --- a/man/systemd-journal-gatewayd.service.xml +++ b/man/systemd-journal-gatewayd.service.xml @@ -58,26 +58,25 @@ - Specify the path to a file containing a server - certificate in PEM format. This option switches - systemd-journal-gatewayd into HTTPS mode - and must be used together with + Specify the path to a file or AF_UNIX stream socket to read the + server certificate from. The certificate must be in PEM format. This option switches + systemd-journal-gatewayd into HTTPS mode and must be used together with . - Specify the path to a file containing a server - key in PEM format corresponding to the certificate specified - with . + Specify the path to a file or AF_UNIX stream socket to read the + server key corresponding to the certificate specified with from. The key + must be in PEM format. - Specify the path to a file containing a - CA certificate in PEM format. + Specify the path to a file or AF_UNIX stream socket to read a CA + certificate from. The certificate must be in PEM format. @@ -217,9 +216,9 @@ where - is a cursor string, - is an integer, - is an unsigned integer. + cursor is a cursor string, + num_skip is an integer, + num_entries is an unsigned integer. Range defaults to all available events. diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml index b28092d18..1db0128f7 100644 --- a/man/systemd-journal-remote.service.xml +++ b/man/systemd-journal-remote.service.xml @@ -180,33 +180,29 @@ - - Takes a path to a SSL key file in PEM format. - Defaults to &CERTIFICATE_ROOT;/private/journal-remote.pem. - This option can be used with . - + Takes a path to a SSL key file in PEM format. Defaults to + &CERTIFICATE_ROOT;/private/journal-remote.pem. This option can be used with + . If the path refers to an AF_UNIX stream socket + in the file system a connection is made to it and the key read from it. - - Takes a path to a SSL certificate file in PEM format. - Defaults to &CERTIFICATE_ROOT;/certs/journal-remote.pem. - This option can be used with . - + Takes a path to a SSL certificate file in PEM format. Defaults to + &CERTIFICATE_ROOT;/certs/journal-remote.pem. This option can be used with + . If the path refers to an AF_UNIX stream socket + in the file system a connection is made to it and the certificate read from it. - - Takes a path to a SSL CA certificate file in PEM format, - or . If is set, - then certificate checking will be disabled. - Defaults to &CERTIFICATE_ROOT;/ca/trusted.pem. - This option can be used with . - + Takes a path to a SSL CA certificate file in PEM format, or . If + is set, then certificate checking will be disabled. Defaults to + &CERTIFICATE_ROOT;/ca/trusted.pem. This option can be used with + . If the path refers to an AF_UNIX stream socket + in the file system a connection is made to it and the certificate read from it. diff --git a/man/systemd-journal-upload.service.xml b/man/systemd-journal-upload.service.xml index 1b8b0be67..174bef803 100644 --- a/man/systemd-journal-upload.service.xml +++ b/man/systemd-journal-upload.service.xml @@ -165,7 +165,9 @@ - Takes a path to a SSL key file in PEM format. + Takes a path to a SSL key file in PEM format, or . + If is set, then client certificate authentication checking + will be disabled. Defaults to &CERTIFICATE_ROOT;/private/journal-upload.pem. @@ -174,7 +176,9 @@ - Takes a path to a SSL certificate file in PEM format. + Takes a path to a SSL certificate file in PEM format, or . + If is set, then client certificate authentication checking + will be disabled. Defaults to &CERTIFICATE_ROOT;/certs/journal-upload.pem. @@ -183,9 +187,8 @@ - Takes a path to a SSL CA certificate file in PEM format, - or . If is set, - then certificate checking will be disabled. + Takes a path to a SSL CA certificate file in PEM format, or /. + If / is set, then certificate checking will be disabled. Defaults to &CERTIFICATE_ROOT;/ca/trusted.pem. @@ -264,7 +267,7 @@ openssl ca -batch -config ca.conf -notext -in $CLIENT.csr -out $CLIENT.pem those files can be specified using TrustedCertificateFile=, ServerCertificateFile=, - ServerKeyFile=, in + and ServerKeyFile= in /etc/systemd/journal-remote.conf and /etc/systemd/journal-upload.conf, respectively. The default locations can be queried by using diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index f4ce4e4fe..6b7a0bc4b 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -106,18 +106,19 @@ systemd-tmpfiles --create --prefix /var/log/journal errors. In order to react gracefully in this case it is recommended that programs logging to standard output/error ignore such errors. If the SIGPIPE UNIX signal handler is not blocked or turned off, such write attempts will also result in such process signals being generated, see - signal7. To mitigate this issue, - systemd service manager explicitly turns off the SIGPIPE signal for all invoked processes by - default (this may be changed for each unit individually via the IgnoreSIGPIPE= option, see + signal7. + To mitigate this issue, systemd service manager explicitly turns off the SIGPIPE + signal for all invoked processes by default (this may be changed for each unit individually via the + IgnoreSIGPIPE= option, see systemd.exec5 for - details). After the standard output/standard error streams have been terminated they may not be recovered until the - services they are associated with are restarted. Note that during normal operation, - systemd-journald.service stores copies of the file descriptors for those streams in the - service manager. If systemd-journald.service is restarted using systemctl - restart or equivalent operation instead of a pair of separate systemctl stop and - systemctl start commands (or equivalent operations), these stream connections are not terminated - and survive the restart. It is thus safe to restart systemd-journald.service, but stopping it - is not recommended. + details). After the standard output/standard error streams have been terminated they may not be recovered + until the services they are associated with are restarted. Note that during normal operation, + systemd-journald.service stores copies of the file descriptors for those streams in + the service manager. If systemd-journald.service is restarted using + systemctl restart or equivalent operation instead of a pair of separate + systemctl stop and systemctl start commands (or equivalent + operations), these stream connections are not terminated and survive the restart. It is thus safe to + restart systemd-journald.service, but stopping it is not recommended. Note that the log record metadata for records transferred via such standard output/error streams reflect the metadata of the peer the stream was originally created for. If the stream connection is passed on to other diff --git a/man/systemd-localed.service.xml b/man/systemd-localed.service.xml index f620aade1..49eeeffee 100644 --- a/man/systemd-localed.service.xml +++ b/man/systemd-localed.service.xml @@ -29,7 +29,7 @@ Description - systemd-localed is a system service + systemd-localed.service is a system service that may be used as mechanism to change the system locale settings, as well as the console key mapping and default X11 key mapping. systemd-localed is automatically @@ -40,10 +40,11 @@ localectl1 is a command line client to this service. - See the - developer documentation for information about the APIs - systemd-localed provides. + See + org.freedesktop.locale15 + and + org.freedesktop.LogControl15 + for a description of the D-Bus API. diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml index a66c53d07..bbfcad4af 100644 --- a/man/systemd-logind.service.xml +++ b/man/systemd-logind.service.xml @@ -78,14 +78,22 @@ for information about the basic concepts of logind such as users, sessions and seats. - See the - logind D-Bus API Documentation for information about the - APIs systemd-logind provides. + See + org.freedesktop.login15 + and + org.freedesktop.LogControl15 + for information about the D-Bus APIs systemd-logind provides. For more information on the inhibition logic see the Inhibitor Lock Developer Documentation. + + If you are interested in writing a display manager that makes use of logind, please have look at + Writing Display + Managers. + If you are interested in writing a desktop environment that makes use of logind, please have look at + Writing + Desktop Environments. @@ -95,7 +103,7 @@ systemd-user-sessions.service8, loginctl1, logind.conf5, - pam_systemd8 + pam_systemd8, sd-login3 diff --git a/man/systemd-machined.service.xml b/man/systemd-machined.service.xml index 8771434e5..a976c606b 100644 --- a/man/systemd-machined.service.xml +++ b/man/systemd-machined.service.xml @@ -29,24 +29,100 @@ Description - systemd-machined is a system service that - keeps track of virtual machines and containers, and processes - belonging to them. + systemd-machined is a system service that keeps track of locally running virtual + machines and containers. + + systemd-machined is useful for registering and keeping track of both OS + containers (containers that share the host kernel but run a full init system of their own and behave in + most regards like a full virtual operating system rather than just one virtualized app) and full virtual + machines (virtualized hardware running normal operating systems and possibly different kernels). + + systemd-machined should not be used for registering/keeping + track of application sandbox containers. A machine in the context of + systemd-machined is supposed to be an abstract term covering both OS containers and + full virtual machines, but not application sandboxes. + + Machines registered with machined are exposed in various ways in the system. For example: + + Tools like + ps1 + will show to which machine a specific process belongs in a column of + its own, and so will + gnome-system-monitor or + systemd-cgls1. + + + systemd's various tools + (systemctl1, + journalctl1, + loginctl1, + hostnamectl1, + timedatectl1, + localectl1, + machinectl1, ...) + support the switch to operate on local containers instead of the host system. + + + systemctl list-machines will show the system state of all local + containers, connecting to the container's init system for that. + + systemctl's switch has the effect of not only showing the + locally running services, but recursively showing the services of all registered containers. + + The machinectl command provides access to a number of useful + operations on registered containers, such as introspecting them, rebooting, shutting them down, and + getting a login prompt on them. + + The + sd-bus3 library + exposes the + sd_bus_open_system_machine3 + call to connect to the system bus of any registered container. + + The + nss-mymachines8 + module makes sure all registered containers can be resolved via normal glibc + gethostbyname3 + or + getaddrinfo3 + calls. + See systemd-nspawn1 for some examples on how to run containers with OS tools. - Use - nss-mymachines8 - to make the names of local containers known to - systemd-machined locally resolvable as host - names. + If you are interested in writing a VM or container manager that makes use of machined, please have + look at Writing + Virtual Machine or Container Managers. Also see the New Control Group + Interfaces. - See the - - machined D-Bus API Documentation for information about the - APIs systemd-machined provides. + The daemon provides both a C library interface + (which is shared with systemd-logind.service8) + as well as a D-Bus interface. + The library interface may be used to introspect and watch the state of virtual machines/containers. + The bus interface provides the same but in addition may also be used to register or terminate + machines. + For more information please consult + sd-login3 + and + org.freedesktop.machine15. + and + org.freedesktop.LogControl15 + + + A small companion daemon + systemd-importd.service8 + is also available, which implements importing, exporting, and downloading of container and VM images. + + + For each container registered with systemd-machined.service that employs user + namespacing, users/groups are synthesized for the used UIDs/GIDs. These are made available to the system + using the User/Group Record Lookup API via + Varlink, and thus may be resolved with + userdbctl1 or the + usual glibc NSS calls. diff --git a/man/systemd-modules-load.service.xml b/man/systemd-modules-load.service.xml index ca9edef36..e960609c6 100644 --- a/man/systemd-modules-load.service.xml +++ b/man/systemd-modules-load.service.xml @@ -29,14 +29,15 @@ Description - systemd-modules-load.service is an - early boot service that loads kernel modules based on static - configuration. + systemd-modules-load.service is an early boot service that loads kernel + modules. It reads static configuration from files in /usr and + /etc, but also runtime configuration from /run and the kernel + command line (see below). See - modules-load.d5 - for information about the configuration of this service. - + modules-load.d5 for + information about the configuration format of this service and paths where configuration files can be + created. diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml index a6e6dd9b4..f1089eed3 100644 --- a/man/systemd-mount.xml +++ b/man/systemd-mount.xml @@ -50,10 +50,11 @@ WHERE. In many ways, systemd-mount is similar to the lower-level - mount8 command, however instead - of executing the mount operation directly and immediately, systemd-mount schedules it through - the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system - checker to execute a priori), and may make use of the auto-mounting logic. + mount8 + command, however instead of executing the mount operation directly and immediately, + systemd-mount schedules it through the service manager job queue, so that it may pull + in further dependencies (such as parent mounts, or a file system checker to execute a priori), and may + make use of the auto-mounting logic. The command takes either one or two arguments. If only one argument is specified it should refer to a block device or regular file containing a file system (e.g. /dev/sdb1 or @@ -61,15 +62,15 @@ label and other metadata, and is mounted to a directory below /run/media/system/ whose name is generated from the file system label. In this mode the block device or image file must exist at the time of invocation of the command, so that it may be probed. If the device is found to be a - removable block device (e.g. a USB stick) an automount point instead of a regular mount point is created + removable block device (e.g. a USB stick), an automount point is created instead of a regular mount point (i.e. the option is implied, see below). - If two arguments are specified the first indicates the mount source (the WHAT) and - the second indicates the path to mount it on (the WHERE). In this mode no probing of the - source is attempted, and a backing device node doesn't have to exist yet. However, if this mode is combined with - , device node probing for additional metadata is enabled, and – much like in the - single-argument case discussed above – the specified device has to exist at the time of invocation of the - command. + If two arguments are specified, the first indicates the mount source (the + WHAT) and the second indicates the path to mount it on (the + WHERE). In this mode no probing of the source is attempted, and a backing + device node doesn't have to exist. However, if this mode is combined with , + device node probing for additional metadata is enabled, and – much like in the single-argument case + discussed above – the specified device has to exist at the time of invocation of the command. Use the command to show a terse table of all local, known block devices with file systems that may be mounted with this command. diff --git a/man/systemd-network-generator.service.xml b/man/systemd-network-generator.service.xml index fcb3c69ff..4a3f78a9e 100644 --- a/man/systemd-network-generator.service.xml +++ b/man/systemd-network-generator.service.xml @@ -96,7 +96,7 @@ systemd1, systemd-networkd.service8, - dracut8 + dracut8 diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml index 94a7bfd5e..2913bb953 100644 --- a/man/systemd-networkd.service.xml +++ b/man/systemd-networkd.service.xml @@ -92,7 +92,7 @@ systemd.network5, systemd.netdev5, systemd-networkd-wait-online.service8, - systemd-networkd-generator.service8 + systemd-network-generator.service8 diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml index 89060680f..6d583003b 100644 --- a/man/systemd-notify.xml +++ b/man/systemd-notify.xml @@ -54,15 +54,19 @@ off the process, i.e. on all processes that match NotifyAccess= or NotifyAccess=. Conversely, if an auxiliary process of the unit sends an sd_notify() message and immediately exits, the service manager might not be able to properly - attribute the message to the unit, and thus will ignore it, even if - NotifyAccess= is set for it. + attribute the message to the unit, and thus will ignore it, even if NotifyAccess= is set for it. When is used, all synchronization for reception of notifications + is disabled, and hence the aforementioned race may occur if the invoking process is not the service manager or spawned + by the service manager. + + Hence, systemd-notify will first attempt to invoke sd_notify() + pretending to have the PID of the invoking process. This will only succeed when invoked with sufficient privileges. + On failure, it will then fall back to invoking it under its own PID. This behaviour is useful in order that when + the tool is invoked from a shell script the shell process — and not the systemd-notify process + — appears as sender of the message, which in turn is helpful if the shell process is the main process of a service, + due to the limitations of NotifyAccess=. Use the + switch to tweak this behaviour. - systemd-notify will first attempt to invoke sd_notify() pretending to - have the PID of the invoking process. This will only succeed when invoked with sufficient privileges. On failure, - it will then fall back to invoking it under its own PID. This behaviour is useful in order that when the tool is - invoked from a shell script the shell process — and not the systemd-notify process — appears as - sender of the message, which in turn is helpful if the shell process is the main process of a service, due to the - limitations of NotifyAccess= described above. @@ -84,12 +88,13 @@ - Inform the init system about the main PID of - the daemon. Takes a PID as argument. If the argument is - omitted, the PID of the process that invoked - systemd-notify is used. This is equivalent - to systemd-notify MAINPID=$PID. For details - about the semantics of this option see + Inform the service manager about the main PID of the daemon. Takes a PID as + argument. If the argument is specified as auto or omitted, the PID of the process + that invoked systemd-notify is used, except if that's the service manager. If the + argument is specified as self, the PID of the systemd-notify + command itself is used, and if parent is specified the calling process' PID is + used — even if it is the service manager. This is equivalent to systemd-notify + MAINPID=$PID. For details about the semantics of this option see sd_notify3. @@ -128,6 +133,17 @@ with systemd. + + + + Do not synchronously wait for the requested operation to finish. + Use of this option is only recommended when systemd-notify + is spawned by the service manager, or when the invoking process is directly spawned + by the service manager and has enough privileges to allow systemd-notify + to send the notification on its behalf. Sending notifications with + this option set is prone to race conditions in all other cases. + + diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index b269b9917..69558ac85 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1,8 +1,8 @@ - + + ]> @@ -238,7 +238,7 @@ all subdirectories and subvolumes below it, but excluding any sub-mounts. May not be specified together with or . - Note that this switch leaves host name, machine ID and + Note that this switch leaves hostname, machine ID and all other settings that could identify the instance unmodified. @@ -250,7 +250,7 @@ If specified, the container is run with a temporary snapshot of its file system that is removed immediately when the container terminates. May not be specified together with . - Note that this switch leaves host name, machine ID and all other settings that could identify + Note that this switch leaves hostname, machine ID and all other settings that could identify the instance unmodified. Please note that — as with — taking the temporary snapshot is more efficient on file systems that support subvolume snapshots or 'reflinks' natively (btrfs or new xfs) than on more traditional file @@ -302,6 +302,10 @@ hash partitions are set up if the root hash for them is specified using the option. + Single file system images (i.e. file systems without a surrounding partition table) can be opened using + dm-verity if the integrity data is passed using the and + (and optionally ) options. + Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified together with , . @@ -390,8 +394,32 @@ project='man-pages'>xattr7), then the root hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or is not supported by the underlying file system), but a file with the .roothash suffix is - found next to the image file, bearing otherwise the same name, the root hash is read from it and automatically - used, also as formatted hexadecimal characters. + found next to the image file, bearing otherwise the same name (except if the image has the + .raw suffix, in which case the root hash file must not have it in its name), the root hash + is read from it and automatically used, also as formatted hexadecimal characters. + + + + + + Takes a PKCS7 formatted binary signature of the option as a path + to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed + by base64:. The dm-verity volume will only be opened if the signature of the root hash hex + string is valid and done by a public key present in the kernel keyring. If this option is not specified, but a + file with the .roothash.p7s suffix is found next to the image file, bearing otherwise the + same name (except if the image has the .raw suffix, in which case the signature file must + not have it in its name), the signature is read from it and automatically used. + + + + + + Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks + using dm-verity, if a root-hash is passed and if the used image itself does not contains the integrity data. + The integrity data must be matched by the root hash. If this option is not specified, but a file with the + .verity suffix is found next to the image file, bearing otherwise the same name (except if + the image has the .raw suffix, in which case the verity data file must not have it in its name), + the verity data is read from it and automatically used. @@ -503,11 +531,9 @@ - After transitioning into the container, change - to the specified user-defined in the container's user - database. Like all other systemd-nspawn features, this is not - a security feature and provides protection against accidental - destructive operations only. + After transitioning into the container, change to the specified user defined in the + container's user database. Like all other systemd-nspawn features, this is not a security feature and + provides protection against accidental destructive operations only. @@ -532,7 +558,7 @@ With option systemd-nspawn waits for the READY=1 message from the init process in the container before sending its own to systemd. For more details about notifications - see sd_notify3). + see sd_notify3. @@ -984,29 +1010,28 @@ - Takes a boolean argument. Specifies the value of the PR_SET_NO_NEW_PRIVS - flag for the container payload. Defaults to off. When turned on the payload code of the container cannot - acquire new privileges, i.e. the "setuid" file bit as well as file system capabilities will not have an effect - anymore. See prctl2 for details - about this flag. + Takes a boolean argument. Specifies the value of the + PR_SET_NO_NEW_PRIVS flag for the container payload. Defaults to off. When turned + on the payload code of the container cannot acquire new privileges, i.e. the "setuid" file bit as + well as file system capabilities will not have an effect anymore. See prctl2 for + details about this flag. - - - Alter the system call filter applied to containers. Takes a space-separated list of system call - names or group names (the latter prefixed with @, as listed by the - syscall-filter command of + Alter the system call filter + applied to containers. Takes a space-separated list of system call names or group names (the latter + prefixed with @, as listed by the syscall-filter command of systemd-analyze1). Passed - system calls will be permitted. The list may optionally be prefixed by ~, in which case all - listed system calls are prohibited. If this command line option is used multiple times the configured lists are - combined. If both a positive and a negative list (that is one system call list without and one with the - ~ prefix) are configured, the negative list takes precedence over the positive list. Note - that systemd-nspawn always implements a system call whitelist (as opposed to a blacklist), - and this command line option hence adds or removes entries from the default whitelist, depending on the - ~ prefix. Note that the applied system call filter is also altered implicitly if additional - capabilities are passed using the --capabilities=. + system calls will be permitted. The list may optionally be prefixed by ~, in which + case all listed system calls are prohibited. If this command line option is used multiple times the + configured lists are combined. If both a positive and a negative list (that is one system call list + without and one with the ~ prefix) are configured, the negative list takes + precedence over the positive list. Note that systemd-nspawn always implements a + system call allow list (as opposed to a deny list!), and this command line option hence adds or + removes entries from the default allow list, depending on the ~ prefix. Note that + the applied system call filter is also altered implicitly if additional capabilities are passed using + the --capabilities=. @@ -1099,48 +1124,79 @@ - Configures how /etc/resolv.conf inside of the container (i.e. DNS - configuration synchronization from host to container) shall be handled. Takes one of off, - copy-host, copy-static, bind-host, - bind-static, delete or auto. If set to - off the /etc/resolv.conf file in the container is left as it is - included in the image, and neither modified nor bind mounted over. If set to copy-host, the - /etc/resolv.conf file from the host is copied into the container. Similar, if - bind-host is used, the file is bind mounted from the host into the container. If set to - copy-static the static resolv.conf file supplied with - systemd-resolved.service8 is - copied into the container, and correspondingly bind-static bind mounts it there. If set to - delete the /etc/resolv.conf file in the container is deleted if it - exists. Finally, if set to auto the file is left as it is if private networking is turned on - (see ). Otherwise, if systemd-resolved.service is - connectible its static resolv.conf file is used, and if not the host's - /etc/resolv.conf file is used. In the latter cases the file is copied if the image is - writable, and bind mounted otherwise. It's recommended to use copy if the container shall be - able to make changes to the DNS configuration on its own, deviating from the host's settings. Otherwise - bind is preferable, as it means direct changes to /etc/resolv.conf in - the container are not allowed, as it is a read-only bind mount (but note that if the container has enough - privileges, it might simply go ahead and unmount the bind mount anyway). Note that both if the file is bind - mounted and if it is copied no further propagation of configuration is generally done after the one-time early - initialization (this is because the file is usually updated through copying and renaming). Defaults to + Configures how /etc/resolv.conf inside of the container shall be + handled (i.e. DNS configuration synchronization from host to container). Takes one of + off, copy-host, copy-static, + copy-uplink, copy-stub, replace-host, + replace-static, replace-uplink, + replace-stub, bind-host, bind-static, + bind-uplink, bind-stub, delete or + auto. + + If set to off the /etc/resolv.conf file in the + container is left as it is included in the image, and neither modified nor bind mounted over. + + If set to copy-host, the /etc/resolv.conf file from the + host is copied into the container, unless the file exists already and is not a regular file (e.g. a + symlink). Similar, if replace-host is used the file is copied, replacing any + existing inode, including symlinks. Similar, if bind-host is used, the file is + bind mounted from the host into the container. + + If set to copy-static, replace-static or + bind-static the static resolv.conf file supplied with + systemd-resolved.service8 + (specifically: /usr/lib/systemd/resolv.conf) is copied or bind mounted into the + container. + + If set to copy-uplink, replace-uplink or + bind-uplink the uplink resolv.conf file managed by + systemd-resolved.service (specifically: + /run/systemd/resolve/resolv.conf) is copied or bind mounted into the + container. + + If set to copy-stub, replace-stub or + bind-stub the stub resolv.conf file managed by + systemd-resolved.service (specifically: + /run/systemd/resolve/stub-resolv.conf) is copied or bind mounted into the + container. + + If set to delete the /etc/resolv.conf file in the + container is deleted if it exists. + + Finally, if set to auto the file is left as it is if private networking is + turned on (see ). Otherwise, if + systemd-resolved.service is running its stub resolv.conf + file is used, and if not the host's /etc/resolv.conf file. In the latter cases + the file is copied if the image is writable, and bind mounted otherwise. + + It's recommended to use copy-… or replace-… if the + container shall be able to make changes to the DNS configuration on its own, deviating from the + host's settings. Otherwise bind is preferable, as it means direct changes to + /etc/resolv.conf in the container are not allowed, as it is a read-only bind + mount (but note that if the container has enough privileges, it might simply go ahead and unmount the + bind mount anyway). Note that both if the file is bind mounted and if it is copied no further + propagation of configuration is generally done after the one-time early initialization (this is + because the file is usually updated through copying and renaming). Defaults to auto. - Configures how /etc/localtime inside of the container (i.e. local timezone - synchronization from host to container) shall be handled. Takes one of off, - copy, bind, symlink, delete or - auto. If set to off the /etc/localtime file in the - container is left as it is included in the image, and neither modified nor bind mounted over. If set to - copy the /etc/localtime file of the host is copied into the - container. Similar, if bind is used, it is bind mounted from the host into the container. If - set to symlink a symlink from /etc/localtime in the container is - created pointing to the matching the timezone file of the container that matches the timezone setting on the - host. If set to delete the file in the container is deleted, should it exist. If set to - auto and the /etc/localtime file of the host is a symlink, then - symlink mode is used, and copy otherwise, except if the image is - read-only in which case bind is used instead. Defaults to + Configures how /etc/localtime inside of the container + (i.e. local timezone synchronization from host to container) shall be handled. Takes one of + off, copy, bind, symlink, + delete or auto. If set to off the + /etc/localtime file in the container is left as it is included in the image, and + neither modified nor bind mounted over. If set to copy the + /etc/localtime file of the host is copied into the container. Similarly, if + bind is used, the file is bind mounted from the host into the container. If set to + symlink, a symlink is created pointing from /etc/localtime in + the container to the timezone file in the container that matches the timezone setting on the host. If + set to delete, the file in the container is deleted, should it exist. If set to + auto and the /etc/localtime file of the host is a symlink, + then symlink mode is used, and copy otherwise, except if the + image is read-only in which case bind is used instead. Defaults to auto. @@ -1383,7 +1439,7 @@ This installs a minimal Fedora distribution into the directory /var/lib/machines/f&fedora_latest_version; - and then boots an OS in a namespace container in it. Because the installation + and then boots that OS in a namespace container. Because the installation is located underneath the standard /var/lib/machines/ directory, it is also possible to start the machine using systemd-nspawn -M f&fedora_latest_version;. @@ -1397,7 +1453,7 @@ This installs a minimal Debian unstable distribution into the directory ~/debian-tree/ and then - spawns a shell in a namespace container in it. + spawns a shell from this image in a namespace container. debootstrap supports Debian, diff --git a/man/systemd-pstore.service.xml b/man/systemd-pstore.service.xml index 47916da52..335a3b3d1 100644 --- a/man/systemd-pstore.service.xml +++ b/man/systemd-pstore.service.xml @@ -81,6 +81,24 @@ pstore.conf5. + + + Controlling kernel parameters + + The kernel has two parameters, + /sys/module/kernel/parameters/crash_kexec_post_notifiers and + /sys/module/printk/parameters/always_kmsg_dump, + that control writes into pstore. + The crash_kexec_post_notifiers parameter enables the kernel to write + dmesg (including stack trace) into pstore upon a panic or crash, and + printk.always_kmsg_dump parameter enables the kernel to write dmesg + upon a normal shutdown (shutdown, reboot, halt). These kernel + parameters are managed via the + tmpfiles.d5 + mechanism, specifically the file /usr/lib/tmpfiles/systemd-pstore.conf. + + + diff --git a/man/systemd-random-seed.service.xml b/man/systemd-random-seed.service.xml index 28783a15e..a9e322425 100644 --- a/man/systemd-random-seed.service.xml +++ b/man/systemd-random-seed.service.xml @@ -44,7 +44,7 @@ systemd-boot7, with its bootctl random-seed functionality. - When loading the random seed from disk its file is immediately updated with a new seed retrieved + When loading the random seed from disk, the file is immediately updated with a new seed retrieved from the kernel, in order to ensure no two boots operate with the same random seed. This new seed is retrieved synchronously from the kernel, which means the service will not complete start-up until the random pool is fully initialized. On entropy-starved systems this may take a while. This functionality is diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index f55be4f32..ffa88baf6 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -57,9 +57,10 @@ available but not yet used. Specifically the following use cases are among those covered: - The root partition may be grown to cover the whole available disk space - A /home/, swap or /srv partition can be added in - A second (or third, …) root partition may be added in, to cover A/B style setups + The root partition may be grown to cover the whole available disk space. + A /home/, swap or /srv/ partition can be + added. + A second (or third, …) root partition may be added, to cover A/B style setups where a second version of the root file system is alternatingly used for implementing update schemes. The deployed image would carry only a single partition ("A") but on first boot a second partition ("B") for this purpose is automatically created. @@ -69,7 +70,7 @@ The repart.d/*.conf configuration files are loaded and parsed, - and ordered by filename (without the directory suffix). + and ordered by filename (without the directory prefix). The partition table already existing on the block device is loaded and parsed. @@ -119,13 +120,13 @@ As exception to the normally strictly incremental operation, when called in a special "factory - reset" mode systemd-repart may also be used to erase select existing partitions to + reset" mode, systemd-repart may also be used to erase existing partitions to reset an installation back to vendor defaults. This mode of operation is used when either the switch is passed on the tool's command line, or the option specified on the kernel command line, or the FactoryReset EFI variable (vendor UUID 8cf2644b-4b0b-428f-9387-6d876050dc67) is set to "yes". It alters the algorithm above - slightly: between the 3rd and the 4th step above the any partition marked explicitly via the + slightly: between the 3rd and the 4th step above any partition marked explicitly via the FactoryReset= boolean is deleted, and the algorithm restarted, thus immediately re-creating these partitions anew empty. @@ -145,6 +146,12 @@ also be set explicitly, formatted as UUID via the option. By hashing these UUIDs from a common seed images prepared with this tool become reproducible and the result of the algorithm above deterministic. + + The positional argument should specify the block device to operate on. Instead of a block device + node path a regular file may be specified too, in which case the command operates on it like it would if + a loopback block device node was specified with the file attached. If is + specified the specified path is created as regular file, which is useful for generating disk images from + scratch. @@ -165,9 +172,9 @@ Takes one of refuse, allow, - require or force. Controls how to operate on block devices that - are entirely empty, i.e. carry no partition table/disk label yet. If this switch is not specified the - implied default is refuse. + require, force or create. Controls how to + operate on block devices that are entirely empty, i.e. carry no partition table/disk label yet. If + this switch is not specified the implied default is refuse. If refuse systemd-repart requires that the block device it shall operate on already carries a partition table and refuses operation if none is found. If @@ -176,7 +183,9 @@ exists so far, and refuse operation if one already exists. If force it will create a fresh partition table unconditionally, erasing the disk fully in effect. If force no existing partitions will be taken into account or survive the - operation. Hence: use with care, this is a great way to lose all your data. + operation. Hence: use with care, this is a great way to lose all your data. If + create a new loopback file is create under the path passed via the device node + parameter, of the size indicated with , see below. @@ -186,7 +195,20 @@ the implied default. Controls whether to issue the BLKDISCARD I/O control command on the space taken up by any added partitions or on the space in between them. Usually, it's a good idea to issue this request since it tells the underlying hardware that the covered blocks - shall be considered empty, improving performance. + shall be considered empty, improving performance. If operating on a regular file instead of a block + device node, a sparse file is generated. + + + + + + Takes a size in bytes, using the usual K, M, G, T suffixes. If used the specified + device node path must refer to a regular file, which is then grown to the specified size if smaller, + before any change is made to the partition table. This is not supported if the specified node is a + block device. This switch has no effect if the file is already as large as the specified size or + larger. The specified size is implicitly rounded up to multiples of 4096. When used with + this specifies the initial size of the loopback file to + create. @@ -246,9 +268,9 @@ - Takes a file system path. If specified the *.conf are directly - read from the specified directory instead of searching in - /usr/lib/repart.d/*.conf, /etc/repart.d/*.conf, + Takes a file system path. If specified the *.conf files are read + from the specified directory instead of searching in /usr/lib/repart.d/*.conf, + /etc/repart.d/*.conf, /run/repart.d/*.conf. diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 53c46a101..914607e3f 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -29,156 +29,192 @@ Description - systemd-resolved is a system service that provides network name resolution to local - applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR and MulticastDNS - resolver and responder. Local applications may submit network name resolution requests via three interfaces: + systemd-resolved is a system service that provides network name resolution to + local applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR + and MulticastDNS resolver and responder. Local applications may submit network name resolution requests + via three interfaces: - The native, fully-featured API systemd-resolved exposes on the bus. See the - API Documentation for - details. Usage of this API is generally recommended to clients as it is asynchronous and fully featured (for - example, properly returns DNSSEC validation status and interface scope for addresses as necessary for supporting - link-local networking). + The native, fully-featured API systemd-resolved exposes on the bus, + see + org.freedesktop.resolve15 + and + org.freedesktop.LogControl15 + for details. Usage of this API is generally recommended to clients as it is asynchronous and fully + featured (for example, properly returns DNSSEC validation status and interface scope for addresses as + necessary for supporting link-local networking). The glibc - getaddrinfo3 API as defined - by RFC3493 and its related resolver functions, - including gethostbyname3. This - API is widely supported, including beyond the Linux platform. In its current form it does not expose DNSSEC - validation status information however, and is synchronous only. This API is backed by the glibc Name Service - Switch (nss5). Usage of the - glibc NSS module nss-resolve8 - is required in order to allow glibc's NSS resolver functions to resolve host names via + getaddrinfo3 + API as defined by RFC3493 and its related + resolver functions, including + gethostbyname3. + This API is widely supported, including beyond the Linux platform. In its current form it does not + expose DNSSEC validation status information however, and is synchronous only. This API is backed by the + glibc Name Service Switch + (nss5). + Usage of the glibc NSS module + nss-resolve8 is + required in order to allow glibc's NSS resolver functions to resolve hostnames via systemd-resolved. - Additionally, systemd-resolved provides a local DNS stub listener on IP - address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly, bypassing any local - API may be directed to this stub, in order to connect them to systemd-resolved. Note however - that it is strongly recommended that local programs use the glibc NSS or bus APIs instead (as described above), - as various network resolution concepts (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped - to the unicast DNS protocol. + Additionally, systemd-resolved provides a local DNS stub listener on + IP address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly, + bypassing any local API may be directed to this stub, in order to connect them to + systemd-resolved. Note however that it is strongly recommended that local programs + use the glibc NSS or bus APIs instead (as described above), as various network resolution concepts + (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped to the unicast DNS + protocol. The DNS servers contacted are determined from the global settings in /etc/systemd/resolved.conf, the per-link static settings in /etc/systemd/network/*.network files (in case - systemd-networkd.service8 is - used), the per-link dynamic settings received over DHCP, user request made via - resolvectl1, and any DNS server - information made available by other system services. See + systemd-networkd.service8 + is used), the per-link dynamic settings received over DHCP, information provided via + resolvectl1, and any + DNS server information made available by other system services. See resolved.conf5 and - systemd.network5 for details - about systemd's own configuration files for DNS servers. To improve compatibility, - /etc/resolv.conf is read in order to discover configured system DNS servers, but only if it is - not a symlink to /run/systemd/resolve/stub-resolv.conf, - /usr/lib/systemd/resolv.conf or /run/systemd/resolve/resolv.conf (see - below). + systemd.network5 for + details about systemd's own configuration files for DNS servers. To improve compatibility, + /etc/resolv.conf is read in order to discover configured system DNS servers, but + only if it is not a symlink to /run/systemd/resolve/stub-resolv.conf, + /usr/lib/systemd/resolv.conf or + /run/systemd/resolve/resolv.conf (see below). Synthetic Records - systemd-resolved synthesizes DNS resource records (RRs) for the following cases: + systemd-resolved synthesizes DNS resource records (RRs) for the following + cases: - The local, configured hostname is resolved to - all locally configured IP addresses ordered by their scope, or - — if none are configured — the IPv4 address 127.0.0.2 (which - is on the local loopback) and the IPv6 address ::1 (which is the - local host). + The local, configured hostname is resolved to all locally configured IP addresses + ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local + loopback) and the IPv6 address ::1 (which is the local host). - The hostnames localhost and - localhost.localdomain (as well as any hostname - ending in .localhost or .localhost.localdomain) - are resolved to the IP addresses 127.0.0.1 and ::1. + The hostnames localhost and localhost.localdomain + (as well as any hostname ending in .localhost or + .localhost.localdomain) are resolved to the IP addresses 127.0.0.1 and ::1. + - The hostname _gateway is - resolved to all current default routing gateway addresses, - ordered by their metric. This assigns a stable hostname to the - current gateway, useful for referencing it independently of the - current network configuration state. + The hostname _gateway is resolved to all current default routing + gateway addresses, ordered by their metric. This assigns a stable hostname to the current gateway, + useful for referencing it independently of the current network configuration state. - The mappings defined in /etc/hosts are resolved - to their configured addresses and back, but they will not affect lookups for - non-address types (like MX). + The mappings defined in /etc/hosts are resolved to their + configured addresses and back, but they will not affect lookups for non-address types (like MX). + Support for /etc/hosts may be disabled with ReadEtcHosts=no, + see resolved.conf5. + Protocols and Routing - Lookup requests are routed to the available DNS servers, LLMNR and MulticastDNS interfaces according to the - following rules: + Lookup requests are routed to the available DNS servers, LLMNR, and MulticastDNS interfaces + according to the following rules: - Lookups for the special hostname localhost are never routed to the network. (A - few other, special domains are handled the same way.) + Names for which synthetic records are generated (as listed in the previous section) are + never routed to the network and a reply is sent immediately. In particular this means that lookups for + localhost are never routed to the network. - Single-label names are routed to all local interfaces capable of IP multicasting, using the LLMNR - protocol. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are only - sent via LLMNR on IPv6. Lookups for the locally configured host name and the _gateway host - name are never routed to LLMNR. + Single-label names are routed to all local interfaces capable of IP multicasting, where + LLMNR is not disabled, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on + IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally + configured hostname and the _gateway hostname are never routed to LLMNR. + - Multi-label names with the domain suffix .local are routed to all local - interfaces capable of IP multicasting, using the MulticastDNS protocol. As with LLMNR IPv4 address lookups are - sent via IPv4 and IPv6 address lookups are sent via IPv6. + Multi-label names with the domain suffix .local are routed to all + local interfaces capable of IP multicasting, where MulticastDNS is not disabled, using the MulticastDNS + protocol. As with LLMNR, IPv4 address lookups are sent via IPv4 and IPv6 address lookups are sent via + IPv6. - Other multi-label names are routed to all local interfaces that have a DNS server configured, - plus the globally configured DNS server if there is one. Address lookups from the link-local address range are - never routed to DNS. Note that by default lookups for domains with the .local suffix are not - routed to DNS servers, unless the domain is specified explicitly as routing or search domain for the DNS server - and interface. This means that on networks where the .local domain is defined in a - site-specific DNS server, explicit search or routing domains need to be configured to make lookups within this - DNS domain work. Note that today it's generally recommended to avoid defining .local in a DNS - server, as RFC6762 reserves this domain for exclusive + Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or + MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means + that such records can be resolved when search domains are defined. For any interface which defines + search domains, such look-ups are routed to that interface, suffixed with each of the search domains + defined on that interface in turn. When global search domains are defined, such look-ups are routed to + all interfaces, suffixed by each of the global search domains in turn. Additionally, lookup of + single-label names via unicast DNS may be enabled with the + ResolveUnicastSingleLabel=yes setting. The details of which servers are queried and + how the final reply is chosen are described below. Note that this means that address queries for + single-label names are never sent out to remote DNS servers by default, and if no search domains are + defined, resolution will fail. + + Other multi-label names are routed to all local interfaces that have a DNS server + configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for + domains with the .local suffix are not routed to DNS servers, unless the domain is + specified explicitly as routing or search domain for the DNS server and interface. This means that on + networks where the .local domain is defined in a site-specific DNS server, explicit + search or routing domains need to be configured to make lookups within this DNS domain work. Note that + these days, it's generally recommended to avoid defining .local in a DNS server, as + RFC6762 reserves this domain for exclusive MulticastDNS use. + + Address lookups are routed similarly to multi-label names, with the exception that + addresses from the link-local address range are never routed to unicast DNS and are only resolved using + LLMNR and MulticastDNS (when enabled). - If lookups are routed to multiple interfaces, the first - successful response is returned (thus effectively merging the - lookup zones on all matching interfaces). If the lookup failed on - all interfaces, the last failing response is returned. + If lookups are routed to multiple interfaces, the first successful response is returned (thus + effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces, + the last failing response is returned. - Routing of lookups may be influenced by configuring per-interface domain names and other settings. See + Routing of lookups may be influenced by configuring per-interface domain names and other + settings. See systemd.network5 and - resolvectl1 for details. The - following query routing logic applies for unicast DNS traffic: + resolvectl1 for + details. The following query routing logic applies for unicast DNS traffic: - If a name to look up matches (that is: is equal to or has as suffix) any of the configured search - or route-only domains of any link (or the globally configured DNS settings), the "best matching" - search/route-only domain is determined: the matching one with the most labels. The query is then sent to all DNS - servers of any links or the globally configured DNS servers associated with this "best matching" - search/route-only domain. (Note that more than one link might have this same "best matching" search/route-only - domain configured, in which case the query is sent to all of them in parallel). + If a name to look up matches (that is: is equal to or has as suffix) any of the + configured search or route-only domains of any link (see + systemd.network5), + or the globally configured DNS settings (see the discussion of Domains= in + resolved.conf5), + "best matching" search/route-only domain is determined: the matching one with the most labels. The + query is then sent to all DNS servers of any links or the globally configured DNS servers associated + with this "best matching" search/route-only domain. (Note that more than one link might have this same + "best matching" search/route-only domain configured, in which case the query is sent to all of them in + parallel). - If a query does not match any configured search/route-only domain (neither per-link nor global), - it is sent to all DNS servers that are configured on links with the "DNS default route" option set, as well as - the globally configured DNS server. + In case of single-label names, when search domains are defined, the same logic applies, except + that the name is first suffixed by the search domain. - If there is no link configured as "DNS default route" and no global DNS server configured, the - compiled-in fallback DNS server is used. + If a query does not match any configured search/route-only domain (neither per-link nor + global), it is sent to all DNS servers that are configured on links with the "DNS default route" option + set, as well as the globally configured DNS server. - Otherwise the query is failed as no suitable DNS servers could be determined. + If there is no link configured as "DNS default route" and no global DNS server + configured, the compiled-in fallback DNS server is used. + + Otherwise the query is failed as no suitable DNS servers could be determined. + - The "DNS default route" option is a boolean setting configurable with resolvectl or in - .network files. If not set, it is implicitly determined based on the configured DNS domains - for a link: if there's any route-only domain (not matching ~.) it defaults to false, otherwise - to true. + The "DNS default route" option is a boolean setting configurable with resolvectl + or in .network files. If not set, it is implicitly determined based on the + configured DNS domains for a link: if there's any route-only domain (not matching ~.) + it defaults to false, otherwise to true. - Effectively this means: in order to preferably route all DNS queries not explicitly matched by - search/route-only domain configuration to a specific link, configure a ~. route-only domain on - it. This will ensure that other links will not be considered for the queries (unless they too carry such a - route-only domain). In order to route all such DNS queries to a specific link only in case no other link is - preferable, then set the "DNS default route" option for the link to true, and do not configure a - ~. route-only domain on it. Finally, in order to ensure that a specific link never receives any - DNS traffic not matching any of its configured search/route-only domains, set the "DNS default route" option for it - to false. + Effectively this means: in order to support single-label non-synthetized names, define appropriate + search domains. In order to preferably route all DNS queries not explicitly matched by search/route-only + domain configuration to a specific link, configure a ~. route-only domain on it. This + will ensure that other links will not be considered for these queries (unless they too carry such a + route-only domain). In order to route all such DNS queries to a specific link only if no other link + is preferable, set the "DNS default route" option for the link to true and do not configure a + ~. route-only domain on it. Finally, in order to ensure that a specific link never + receives any DNS traffic not matching any of its configured search/route-only domains, set the "DNS + default route" option for it to false. - See the resolved D-Bus API - Documentation for information about the APIs systemd-resolved provides. + See the resolved D-Bus API + Documentation for information about the APIs systemd-resolved provides. + @@ -190,40 +226,41 @@ systemd-resolved maintains the - /run/systemd/resolve/stub-resolv.conf file for compatibility with traditional Linux - programs. This file may be symlinked from /etc/resolv.conf. This file lists the 127.0.0.53 - DNS stub (see above) as the only DNS server. It also contains a list of search domains that are in use by - systemd-resolved. The list of search domains is always kept up-to-date. Note that - /run/systemd/resolve/stub-resolv.conf should not be used directly by applications, but only - through a symlink from /etc/resolv.conf. This file may be symlinked from - /etc/resolv.conf in order to connect all local clients that bypass local DNS APIs to - systemd-resolved with correct search domains settings. This mode of operation is + /run/systemd/resolve/stub-resolv.conf file for compatibility with traditional + Linux programs. This file may be symlinked from /etc/resolv.conf. This file lists + the 127.0.0.53 DNS stub (see above) as the only DNS server. It also contains a list of search domains + that are in use by systemd-resolved. The list of search domains is always kept up-to-date. Note that + /run/systemd/resolve/stub-resolv.conf should not be used directly by applications, + but only through a symlink from /etc/resolv.conf. This file may be symlinked from + /etc/resolv.conf in order to connect all local clients that bypass local DNS APIs + to systemd-resolved with correct search domains settings. This mode of operation is recommended. A static file /usr/lib/systemd/resolv.conf is provided that lists the 127.0.0.53 DNS stub (see above) as only DNS server. This file may be symlinked from - /etc/resolv.conf in order to connect all local clients that bypass local DNS APIs to - systemd-resolved. This file does not contain any search domains. + /etc/resolv.conf in order to connect all local clients that bypass local DNS APIs + to systemd-resolved. This file does not contain any search domains. + systemd-resolved maintains the /run/systemd/resolve/resolv.conf file for compatibility with traditional Linux - programs. This file may be symlinked from /etc/resolv.conf and is always kept up-to-date, - containing information about all known DNS servers. Note the file format's limitations: it does not know a - concept of per-interface DNS servers and hence only contains system-wide DNS server definitions. Note that - /run/systemd/resolve/resolv.conf should not be used directly by applications, but only - through a symlink from /etc/resolv.conf. If this mode of operation is used local clients - that bypass any local DNS API will also bypass systemd-resolved and will talk directly to the - known DNS servers. + programs. This file may be symlinked from /etc/resolv.conf and is always kept + up-to-date, containing information about all known DNS servers. Note the file format's limitations: it + does not know a concept of per-interface DNS servers and hence only contains system-wide DNS server + definitions. Note that /run/systemd/resolve/resolv.conf should not be used + directly by applications, but only through a symlink from /etc/resolv.conf. If + this mode of operation is used local clients that bypass any local DNS API will also bypass + systemd-resolved and will talk directly to the known DNS servers. - Alternatively, /etc/resolv.conf may be managed by other packages, in which - case systemd-resolved will read it for DNS configuration data. In this mode of operation - systemd-resolved is consumer rather than provider of this configuration + Alternatively, /etc/resolv.conf may be managed by other packages, + in which case systemd-resolved will read it for DNS configuration data. In this mode + of operation systemd-resolved is consumer rather than provider of this configuration file. - Note that the selected mode of operation for this file is detected fully automatically, depending on whether - /etc/resolv.conf is a symlink to /run/systemd/resolve/resolv.conf or - lists 127.0.0.53 as DNS server. + Note that the selected mode of operation for this file is detected fully automatically, depending + on whether /etc/resolv.conf is a symlink to + /run/systemd/resolve/resolv.conf or lists 127.0.0.53 as DNS server. @@ -234,20 +271,21 @@ SIGUSR1 Upon reception of the SIGUSR1 process signal - systemd-resolved will dump the contents of all DNS resource record caches it maintains, as - well as all feature level information it learnt about configured DNS servers into the system - logs. + systemd-resolved will dump the contents of all DNS resource record caches it + maintains, as well as all feature level information it learnt about configured DNS servers into the + system logs. SIGUSR2 Upon reception of the SIGUSR2 process signal - systemd-resolved will flush all caches it maintains. Note that it should normally not be - necessary to request this explicitly – except for debugging purposes – as systemd-resolved - flushes the caches automatically anyway any time the host's network configuration changes. Sending this signal - to systemd-resolved is equivalent to the resolvectl flush-caches - command, however the latter is recommended since it operates in a synchronous way. + systemd-resolved will flush all caches it maintains. Note that it should normally + not be necessary to request this explicitly – except for debugging purposes – as + systemd-resolved flushes the caches automatically anyway any time the host's + network configuration changes. Sending this signal to systemd-resolved is + equivalent to the resolvectl flush-caches command, however the latter is + recommended since it operates in a synchronous way. @@ -255,13 +293,14 @@ Upon reception of the SIGRTMIN+1 process signal systemd-resolved will forget everything it learnt about the configured DNS - servers. Specifically any information about server feature support is flushed out, and the server feature - probing logic is restarted on the next request, starting with the most fully featured level. Note that it - should normally not be necessary to request this explicitly – except for debugging purposes – as - systemd-resolved automatically forgets learnt information any time the DNS server - configuration changes. Sending this signal to systemd-resolved is equivalent to the - resolvectl reset-server-features command, however the latter is recommended since it - operates in a synchronous way. + servers. Specifically any information about server feature support is flushed out, and the server + feature probing logic is restarted on the next request, starting with the most fully featured + level. Note that it should normally not be necessary to request this explicitly – except for + debugging purposes – as systemd-resolved automatically forgets learnt information + any time the DNS server configuration changes. Sending this signal to + systemd-resolved is equivalent to the resolvectl + reset-server-features command, however the latter is recommended since it operates in a + synchronous way. diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 6fc2c8d0e..a88f60fbb 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -153,6 +153,20 @@ + + + + Make the new .service or .scope unit part + of the inherited slice. This option can be combined with . + + An inherited slice is located within systemd-run slice. Example: if + systemd-run slice is foo.slice, and the + argument is bar, the unit will be placed under the + foo-bar.slice. + + + + diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml index a6949b0c3..a3714f52a 100644 --- a/man/systemd-sleep.conf.xml +++ b/man/systemd-sleep.conf.xml @@ -95,7 +95,7 @@ systemd1 attempts to suspend or hibernate the machine. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -105,7 +105,7 @@ Options The following options can be configured in the - [Sleep] section of + [Sleep] section of /etc/systemd/sleep.conf or a sleep.conf.d file: diff --git a/man/systemd-socket-activate.xml b/man/systemd-socket-activate.xml index f3dbb47a6..20a557219 100644 --- a/man/systemd-socket-activate.xml +++ b/man/systemd-socket-activate.xml @@ -141,6 +141,7 @@ $SYSTEMD_LOG_TARGET $SYSTEMD_LOG_LEVEL + $SYSTEMD_LOG_TIME $SYSTEMD_LOG_COLOR $SYSTEMD_LOG_LOCATION diff --git a/man/systemd-socket-proxyd.xml b/man/systemd-socket-proxyd.xml index a72ac1bbc..a4e18989f 100644 --- a/man/systemd-socket-proxyd.xml +++ b/man/systemd-socket-proxyd.xml @@ -16,7 +16,7 @@ systemd-socket-proxyd - Bidirectionally proxy local sockets to another (possibly remote) socket. + Bidirectionally proxy local sockets to another (possibly remote) socket @@ -67,6 +67,13 @@ Sets the maximum number of simultaneous connections, defaults to 256. If the limit of concurrent connections is reached further connections will be refused. + + + + Sets the time before exiting when there are no connections, defaults to + infinity. Takes a unit-less value in seconds, or a time span value such + as 5min 20s. + @@ -115,6 +122,9 @@ server { + If nginx.service has StopWhenUnneeded= set, then + passing to systemd-socket-proxyd allows + both services to stop during idle periods. Namespace Example diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml index 2310e6f52..e57000e09 100644 --- a/man/systemd-suspend.service.xml +++ b/man/systemd-suspend.service.xml @@ -71,19 +71,16 @@ url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor interface. - Note that - systemd-suspend.service, - systemd-hibernate.service, and - systemd-hybrid-sleep.service - systemd-suspend-then-hibernate.service - should never be executed directly. Instead, trigger system sleep - states with a command such as systemctl suspend - or similar. + Note that systemd-suspend.service, + systemd-hibernate.service, systemd-hybrid-sleep.service, and + systemd-suspend-then-hibernate.service should never be executed directly. Instead, + trigger system sleep with a command such as systemctl suspend or systemctl + hibernate. Internally, this service will echo a string like mem into /sys/power/state, to trigger the actual system suspend. What exactly is written - where can be configured in the [Sleep] section + where can be configured in the [Sleep] section of /etc/systemd/sleep.conf or a sleep.conf.d file. See systemd-sleep.conf5. diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index e22b335d3..c64e57c27 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -48,7 +48,7 @@ user.conf.d directories. These configuration files contain a few settings controlling basic manager operations. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -58,15 +58,16 @@ Options All options are configured in the - [Manager] section: + [Manager] section: - LogLevel= - LogTarget= LogColor= + LogLevel= LogLocation= + LogTarget= + LogTime= DumpCore=yes CrashChangeVT=no CrashShell=no @@ -148,7 +149,7 @@ for details. During the first phase of the shutdown operation the system and service manager remains running and hence RuntimeWatchdogSec= is still honoured. In order to define a timeout on this first phase of system shutdown, configure JobTimeoutSec= and JobTimeoutAction= - in the [Unit] section of the shutdown.target unit. By default + in the [Unit] section of the shutdown.target unit. By default RuntimeWatchdogSec= defaults to 0 (off), and RebootWatchdogSec= to 10min. KExecWatchdogSec= may be used to additionally enable the watchdog when kexec is being executed rather than when rebooting. Note that if the kernel does not reset the watchdog on kexec (depending @@ -386,9 +387,9 @@ units. See setrlimit2 for details. These settings may be overridden in individual units using the corresponding - LimitXXX= directives, see - systemd.exec5, for - details, and they accept the same parameter syntax. Note that these resource limits are only defaults + LimitXXX= directives and they accept the same parameter syntax, + see systemd.exec5 + for details. Note that these resource limits are only defaults for units, they are not applied to the service manager process (i.e. PID 1) itself. diff --git a/man/systemd-sysv-generator.xml b/man/systemd-sysv-generator.xml index 795d9eebe..7396d4ab6 100644 --- a/man/systemd-sysv-generator.xml +++ b/man/systemd-sysv-generator.xml @@ -43,7 +43,7 @@ $named, $portmap, $time are supported and will be turned into dependencies on specific native systemd targets. See - systemd.special5 + systemd.special7 for more details. SysV runlevels have corresponding systemd targets diff --git a/man/systemd-time-wait-sync.service.xml b/man/systemd-time-wait-sync.service.xml index c8a92f94a..685fe7433 100644 --- a/man/systemd-time-wait-sync.service.xml +++ b/man/systemd-time-wait-sync.service.xml @@ -18,7 +18,7 @@ systemd-time-wait-sync.service systemd-time-wait-sync - Wait Until Kernel Time Synchronized + Wait until kernel time is synchronized diff --git a/man/systemd-timedated.service.xml b/man/systemd-timedated.service.xml index f981848cb..93cfdf511 100644 --- a/man/systemd-timedated.service.xml +++ b/man/systemd-timedated.service.xml @@ -29,7 +29,7 @@ Description - systemd-timedated is a system service + systemd-timedated.service is a system service that may be used as a mechanism to change the system clock and timezone, as well as to enable/disable network time synchronization. systemd-timedated is automatically activated @@ -39,10 +39,25 @@ timedatectl1 is a command line client to this service. - See the - developer documentation for information about the APIs - systemd-timedated provides. + systemd-timedated currently offers access to + the following four settings: + + The system time + + The system timezone + + A boolean controlling whether the system RTC is in local or UTC + timezone + + Whether the time synchronization service is enabled/started or + disabled/stopped, see next section. + + + See + org.freedesktop.timedate15 + and + org.freedesktop.LogControl15 + for information about the D-Bus API. diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 7720ef53f..998fd0911 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -161,10 +161,10 @@ Takes a directory path as an argument. All paths will be prefixed with the given alternate root path, including config search paths. - Note that this option does not alter how the users and groups specified in the configuration files are - resolved. With or without this option, users and groups are always resolved according to the host's user and - group databases, any such databases stored under the specified root directories are not - consulted. + When this option is used, the libc Name Service Switch (NSS) is bypassed for resolving users + and groups. Instead the files /etc/passwd and /etc/group + inside the alternate root are read directly. This means that users/groups not listed in these files + will not be resolved, i.e. LDAP NIS and other complex databases are not considered. diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index 08af76005..da7389c73 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -105,6 +105,21 @@ + + + + + Set the signal which systemd-udevd will send to + forked off processes after reaching event timeout. The setting can be overridden + at boot time with the kernel command line option + udev.timeout_signal=. Setting to SIGABRT + may be helpful in order to debug worker timeouts. Defaults to + SIGKILL. Note that setting the option on the command line + overrides the setting from the configuration file. + + + + @@ -125,8 +140,8 @@ Kernel command line - Parameters starting with "rd." will be read when - systemd-udevd is used in an initrd. + Parameters prefixed with "rd." will be read when systemd-udevd is used in an + initrd, those without will be processed both in the initrd and on the host. udev.log_priority= rd.udev.log_priority= @@ -160,6 +175,31 @@ terminated due to kernel drivers taking too long to initialize. + + udev.timeout_signal= + rd.udev.timeout_signal= + + Specifies a signal that systemd-udevd will send to + workers on timeout. Note that kernel command line option overrides both the + setting in the configuration file and the one on the program command line. + + + + udev.blockdev_read_only + rd.udev.blockdev_read_only + + If specified, mark all physical block devices read-only as they appear. Synthetic block + devices (such as loopback block devices or device mapper devices) are left as they are. This is + useful to guarantee that the contents of physical block devices remains unmodified during runtime, + for example to implement fully stateless systems, for testing or for recovery situations where + corrupted file systems shall not be corrupted further through accidental modification. + + A block device may be marked writable again by issuing the blockdev + --setrw command, see blockdev8 + for details. + + net.ifnames= diff --git a/man/systemd-update-done.service.xml b/man/systemd-update-done.service.xml index ad412691a..91196dff3 100644 --- a/man/systemd-update-done.service.xml +++ b/man/systemd-update-done.service.xml @@ -58,6 +58,10 @@ touch1 on it. + Note that if the systemd.condition-needs-update= kernel command line option is + used it overrides the ConditionNeedsUpdate= unit condition checks. In that case + systemd-update-done.service will not reset the condition state until a follow-up + reboot where the kernel switch is not specified anymore. diff --git a/man/systemd-xdg-autostart-generator.xml b/man/systemd-xdg-autostart-generator.xml new file mode 100644 index 000000000..07b6e455f --- /dev/null +++ b/man/systemd-xdg-autostart-generator.xml @@ -0,0 +1,57 @@ + + + + + + + + systemd-xdg-autostart-generator + systemd + + + + systemd-xdg-autostart-generator + 8 + + + + systemd-xdg-autostart-generator + User unit generator for XDG autostart files + + + + /usr/lib/systemd/system-generators/systemd-xdg-autostart-generator + + + + Description + + systemd-xdg-autostart-generator is a generator + that creates .service units for + XDG autostart + files. + This permits desktop environments to delegate startup of these applications to + systemd1 + . + + Units created by systemd-xdg-autostart-generator + can be started by the desktop environment using xdg-desktop-autostart.target. + See + systemd.special7 + for more details. + + systemd-xdg-autostart-generator implements + systemd.generator7. + + + + See Also + + systemd1, + systemd.service5, + systemd.target5 + + + + diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index fd65f5da7..29b9bb14e 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The automount specific configuration options - are configured in the [Automount] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The automount specific configuration options + are configured in the [Automount] section. Automount units must be named after the automount directories they control. Example: the automount point /home/lennart must be configured in a unit file diff --git a/man/systemd.device.xml b/man/systemd.device.xml index ae786a329..085fd62bc 100644 --- a/man/systemd.device.xml +++ b/man/systemd.device.xml @@ -36,8 +36,8 @@ systemd.unit5 for the common options of all unit configuration files. The common configuration items are configured in the generic - [Unit] and [Install] - sections. A separate [Device] section does not + [Unit] and [Install] + sections. A separate [Device] section does not exist, since no device-specific options may be configured. systemd will dynamically create device units for all kernel @@ -60,7 +60,7 @@ Device units will be reloaded by systemd whenever the corresponding device generates a changed event. Other units can use ReloadPropagatedFrom= to react - to that event + to that event. diff --git a/man/systemd.dnssd.xml b/man/systemd.dnssd.xml index 1ac760eec..d7e6caddf 100644 --- a/man/systemd.dnssd.xml +++ b/man/systemd.dnssd.xml @@ -3,7 +3,9 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> - + systemd.dnssd @@ -62,7 +64,7 @@ [Service] Section Options - The network service file contains a [Service] + The network service file contains a [Service] section, which specifies a discoverable network service announced in a local network with Multicast DNS broadcasts. @@ -73,7 +75,7 @@ An instance name of the network service as defined in the section 4.1.1 of RFC 6763, e.g. webserver. The option supports simple specifier expansion. The following expansions are understood: - +
Specifiers available @@ -87,26 +89,16 @@ - - %m - Machine ID - The machine ID of the running system, formatted as string. See machine-id5 for more information. - - - %b - Boot ID - The boot ID of the running system, formatted as string. See random4 for more information. - - - %H - Host name - The hostname of the running system. - - - %v - Kernel release - Identical to uname -r output. - + + + + + + + + + +
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index f7995e611..3618b5280 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -139,6 +139,60 @@ PrivateDevices= below, as it may change the setting of DevicePolicy=.
+ Units making use of RootImage= automatically gain an + After= dependency on systemd-udevd.service. + +
+
+ + + RootHash= + + Takes a data integrity (dm-verity) root hash specified in hexadecimal, or the path to a file + containing a root hash in ASCII hexadecimal format. This option enables data integrity checks using dm-verity, + if the used image contains the appropriate integrity data (see above) or if RootVerity= is used. + The specified hash must match the root hash of integrity data, and is usually at least 256 bits (and hence 64 + formatted hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but + the image file carries the user.verity.roothash extended file attribute (see xattr7), then the root + hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or + is not supported by the underlying file system), but a file with the .roothash suffix is + found next to the image file, bearing otherwise the same name (except if the image has the + .raw suffix, in which case the root hash file must not have it in its name), the root hash + is read from it and automatically used, also as formatted hexadecimal characters. + + + + + + RootHashSignature= + + Takes a PKCS7 formatted binary signature of the RootHash= option as a path + to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed + by base64:. The dm-verity volume will only be opened if the signature of the root hash + signature is valid and created by a public key present in the kernel keyring. If this option is not specified, + but a file with the .roothash.p7s suffix is found next to the image file, bearing otherwise + the same name (except if the image has the .raw suffix, in which case the signature file + must not have it in its name), the signature is read from it and automatically used. + + + + + + RootVerity= + + Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks + using dm-verity, if RootImage= is used and a root-hash is passed and if the used image itself + does not contains the integrity data. The integrity data must be matched by the root hash. If this option is not + specified, but a file with the .verity suffix is found next to the image file, bearing otherwise + the same name (except if the image has the .raw suffix, in which case the verity data file must + not have it in its name), the verity data is read from it and automatically used. + + This option is supported only for disk images that contain a single file system, without an + enveloping partition table. Images that contain a GPT partition table should instead include both + root file system and matching Verity data in the same image, implementing the Discoverable Partition Specification. + @@ -278,7 +332,7 @@ files or directories. Moreover ProtectSystem=strict and ProtectHome=read-only are implied, thus prohibiting the service to write to arbitrary file system locations. In order to allow the service to write to certain directories, they - have to be whitelisted using ReadWritePaths=, but care must be taken so that + have to be allow-listed using ReadWritePaths=, but care must be taken so that UID/GID recycling doesn't create security issues involving files created by the service. Use RuntimeDirectory= (see below) in order to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically when the unit is terminated. Use @@ -457,10 +511,11 @@ CapabilityBoundingSet=~CAP_B CAP_C AppArmorProfile= - Takes a profile name as argument. The process executed by the unit will switch to this profile - when started. Profiles must already be loaded in the kernel, or the unit will fail. This result in a non - operation if AppArmor is not enabled. If prefixed by -, all errors will be ignored. This - does not affect commands prefixed with +. + Takes a profile name as argument. The process executed by the unit will switch to + this profile when started. Profiles must already be loaded in the kernel, or the unit will fail. If + prefixed by -, all errors will be ignored. This setting has no effect if AppArmor + is not enabled. This setting not affect commands prefixed with +. + @@ -655,8 +710,39 @@ CapabilityBoundingSet=~CAP_B CAP_C UMask= Controls the file mode creation mask. Takes an access mode in octal notation. See - umask2 for details. Defaults - to 0022. + umask2 for + details. Defaults to 0022 for system units. For units of the user service manager the default value + is inherited from the user instance (whose default is inherited from the system service manager, and + thus also is 0022). Hence changing the default value of a user instance, either via + UMask= or via a PAM module, will affect the user instance itself and all user + units started by the user instance unless a user unit has specified its own + UMask=. + + + + CoredumpFilter= + + Controls which types of memory mappings will be saved if the process dumps core + (using the /proc/pid/coredump_filter file). Takes a + whitespace-separated combination of mapping type names or numbers (with the default base 16). Mapping + type names are private-anonymous, shared-anonymous, + private-file-backed, shared-file-backed, + elf-headers, private-huge, + shared-huge, private-dax, shared-dax, + and the special values all (all types) and default (the + kernel default of private-anonymous + shared-anonymous elf-headers + private-huge). See + core5 + for the meaning of the mapping types. When specified multiple times, all specified masks are + ORed. When not set, or if the empty value is assigned, the inherited value is not changed. + + + Add DAX pages to the dump filter + + CoredumpFilter=default private-dax shared-dax + + @@ -795,7 +881,7 @@ CapabilityBoundingSet=~CAP_B CAP_C in NUMAMask=. For more details on each policy please see, set_mempolicy2. For overall overview of NUMA support in Linux see, - numa7 + numa7. @@ -982,14 +1068,16 @@ CapabilityBoundingSet=~CAP_B CAP_C RootDirectory= or RootImage= these paths always reside on the host and are mounted from there into the unit's file system namespace. - If DynamicUser= is used in conjunction with StateDirectory=, - CacheDirectory= and LogsDirectory= is slightly altered: the directories - are created below /var/lib/private, /var/cache/private and + If DynamicUser= is used in conjunction with + StateDirectory=, the logic for CacheDirectory= and + LogsDirectory= is slightly altered: the directories are created below + /var/lib/private, /var/cache/private and /var/log/private, respectively, which are host directories made inaccessible to - unprivileged users, which ensures that access to these directories cannot be gained through dynamic user ID - recycling. Symbolic links are created to hide this difference in behaviour. Both from perspective of the host - and from inside the unit, the relevant directories hence always appear directly below - /var/lib, /var/cache and /var/log. + unprivileged users, which ensures that access to these directories cannot be gained through dynamic + user ID recycling. Symbolic links are created to hide this difference in behaviour. Both from + perspective of the host and from inside the unit, the relevant directories hence always appear + directly below /var/lib, /var/cache and + /var/log. Use RuntimeDirectory= to manage one or more runtime directories for the unit and bind their lifetime to the daemon runtime. This is particularly useful for unprivileged daemons that cannot create @@ -1064,8 +1152,8 @@ StateDirectory=aaa/bbb ccc clean …, see systemctl1 for details. Takes the usual time values and defaults to infinity, i.e. by default - no time-out is applied. If a time-out is configured the clean operation will be aborted forcibly when - the time-out is reached, potentially leaving resources on disk. + no timeout is applied. If a timeout is configured the clean operation will be aborted forcibly when + the timeout is reached, potentially leaving resources on disk. @@ -1079,12 +1167,13 @@ StateDirectory=aaa/bbb ccc contain symlinks, they are resolved relative to the root directory set with RootDirectory=/RootImage=. - Paths listed in ReadWritePaths= are accessible from within the namespace with the same - access modes as from outside of it. Paths listed in ReadOnlyPaths= are accessible for - reading only, writing will be refused even if the usual file access controls would permit this. Nest - ReadWritePaths= inside of ReadOnlyPaths= in order to provide writable - subdirectories within read-only directories. Use ReadWritePaths= in order to whitelist - specific paths for write access if ProtectSystem=strict is used. + Paths listed in ReadWritePaths= are accessible from within the namespace + with the same access modes as from outside of it. Paths listed in ReadOnlyPaths= + are accessible for reading only, writing will be refused even if the usual file access controls would + permit this. Nest ReadWritePaths= inside of ReadOnlyPaths= in + order to provide writable subdirectories within read-only directories. Use + ReadWritePaths= in order to allow-list specific paths for write access if + ProtectSystem=strict is used. Paths listed in InaccessiblePaths= will be made inaccessible for processes inside the namespace along with everything below them in the file system hierarchy. This may be more restrictive than @@ -1152,8 +1241,8 @@ BindReadOnlyPaths=/var/lib/systemd PrivateTmp= Takes a boolean argument. If true, sets up a new file system namespace for the executed - processes and mounts private /tmp and /var/tmp directories inside it - that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of + processes and mounts private /tmp/ and /var/tmp/ directories inside it + that are not shared by processes outside of the namespace. This is useful to secure access to temporary files of the process, but makes sharing between processes via /tmp or /var/tmp impossible. If this is enabled, all temporary files created by a service in these directories will be removed after the service is stopped. Defaults to false. It is possible to run two or more units within the same @@ -1313,7 +1402,7 @@ BindReadOnlyPaths=/var/lib/systemd this option removes CAP_SYS_TIME and CAP_WAKE_ALARM from the capability bounding set for this unit, installs a system call filter to block calls that can set the clock, and DeviceAllow=char-rtc r is implied. This ensures /dev/rtc0, - /dev/rtc1, etc are made read only to the service. See + /dev/rtc1, etc. are made read-only to the service. See systemd.resource-control5 for the details about DeviceAllow=. @@ -1398,29 +1487,31 @@ BindReadOnlyPaths=/var/lib/systemd RestrictAddressFamilies= - Restricts the set of socket address families accessible to the processes of this unit. Takes a - space-separated list of address family names to whitelist, such as AF_UNIX, - AF_INET or AF_INET6. When prefixed with ~ the - listed address families will be applied as blacklist, otherwise as whitelist. Note that this restricts access - to the socket2 system call - only. Sockets passed into the process by other means (for example, by using socket activation with socket - units, see systemd.socket5) - are unaffected. Also, sockets created with socketpair() (which creates connected AF_UNIX - sockets only) are unaffected. Note that this option has no effect on 32-bit x86, s390, s390x, mips, mips-le, - ppc, ppc-le, pcc64, ppc64-le and is ignored (but works correctly on other ABIs, including x86-64). Note that on - systems supporting multiple ABIs (such as x86/x86-64) it is recommended to turn off alternative ABIs for - services, so that they cannot be used to circumvent the restrictions of this option. Specifically, it is - recommended to combine this option with SystemCallArchitectures=native or similar. If - running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability - (e.g. setting User=nobody), NoNewPrivileges=yes is implied. By default, - no restrictions apply, all address families are accessible to processes. If assigned the empty string, any - previous address family restriction changes are undone. This setting does not affect commands prefixed with - +. + Restricts the set of socket address families accessible to the processes of this + unit. Takes a space-separated list of address family names to allow-list, such as + AF_UNIX, AF_INET or AF_INET6. When + prefixed with ~ the listed address families will be applied as deny list, + otherwise as allow list. Note that this restricts access to the socket2 + system call only. Sockets passed into the process by other means (for example, by using socket + activation with socket units, see + systemd.socket5) + are unaffected. Also, sockets created with socketpair() (which creates connected + AF_UNIX sockets only) are unaffected. Note that this option has no effect on 32-bit x86, s390, s390x, + mips, mips-le, ppc, ppc-le, ppc64, ppc64-le and is ignored (but works correctly on other ABIs, + including x86-64). Note that on systems supporting multiple ABIs (such as x86/x86-64) it is + recommended to turn off alternative ABIs for services, so that they cannot be used to circumvent the + restrictions of this option. Specifically, it is recommended to combine this option with + SystemCallArchitectures=native or similar. If running in user mode, or in system + mode, but without the CAP_SYS_ADMIN capability (e.g. setting + User=nobody), NoNewPrivileges=yes is implied. By default, no + restrictions apply, all address families are accessible to processes. If assigned the empty string, + any previous address family restriction changes are undone. This setting does not affect commands + prefixed with +. Use this option to limit exposure of processes to remote access, in particular via exotic and sensitive network protocols, such as AF_PACKET. Note that in most cases, the local - AF_UNIX address family should be included in the configured whitelist as it is frequently + AF_UNIX address family should be included in the configured allow list as it is frequently used for local communication, including for syslog2 logging. @@ -1438,9 +1529,9 @@ BindReadOnlyPaths=/var/lib/systemd any combination of: cgroup, ipc, net, mnt, pid, user and uts. Any namespace type listed is made accessible to the unit's processes, access to namespace types not listed is - prohibited (whitelisting). By prepending the list with a single tilde character (~) the + prohibited (allow-listing). By prepending the list with a single tilde character (~) the effect may be inverted: only the listed namespace types will be made inaccessible, all unlisted ones are - permitted (blacklisting). If the empty string is assigned, the default namespace restrictions are applied, + permitted (deny-listing). If the empty string is assigned, the default namespace restrictions are applied, which is equivalent to false. This option may appear more than once, in which case the namespace types are merged by OR, or by AND if the lines are prefixed with ~ (see examples below). Internally, this setting limits access to the @@ -1601,14 +1692,14 @@ RestrictNamespaces=~cgroup net points of the file system namespace created for each process of this unit. Other file system namespacing unit settings (see the discussion in PrivateMounts= above) will implicitly disable mount and unmount propagation from the unit's processes towards the host by changing the propagation setting of all mount - points in the unit's file system namepace to first. Setting this option to + points in the unit's file system namespace to first. Setting this option to does not reestablish propagation in that case. If not set – but file system namespaces are enabled through another file system namespace unit setting – mount propagation is used, but — as mentioned — as is applied first, propagation from the unit's processes to the host is still turned off. - It is not recommended to to use mount propagation for units, as this means + It is not recommended to use mount propagation for units, as this means temporary mounts (such as removable media) of the host will stay mounted and thus indefinitely busy in forked off processes, as unmount propagation events won't be received by the file system namespace of the unit. @@ -1630,15 +1721,15 @@ RestrictNamespaces=~cgroup net Takes a space-separated list of system call names. If this setting is used, all system calls executed by the unit processes except for the listed ones will result in immediate - process termination with the SIGSYS signal (whitelisting). (See + process termination with the SIGSYS signal (allow-listing). (See SystemCallErrorNumber= below for changing the default action). If the first character of the list is ~, the effect is inverted: only the listed system calls - will result in immediate process termination (blacklisting). Blacklisted system calls and system call + will result in immediate process termination (deny-listing). Deny-listed system calls and system call groups may optionally be suffixed with a colon (:) and errno error number (between 0 and 4095) or errno name such as EPERM, EACCES or EUCLEAN (see errno3 for a - full list). This value will be returned when a blacklisted system call is triggered, instead of + full list). This value will be returned when a deny-listed system call is triggered, instead of terminating the processes immediately. This value takes precedence over the one given in SystemCallErrorNumber=, see below. If running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. setting @@ -1647,7 +1738,7 @@ RestrictNamespaces=~cgroup net for enforcing a minimal sandboxing environment. Note that the execve, exit, exit_group, getrlimit, rt_sigreturn, sigreturn system calls and the system calls - for querying time and sleeping are implicitly whitelisted and do not need to be listed + for querying time and sleeping are implicitly allow-listed and do not need to be listed explicitly. This option may be specified more than once, in which case the filter masks are merged. If the empty string is assigned, the filter is reset, all prior assignments will have no effect. This does not affect commands prefixed with +. @@ -1665,12 +1756,13 @@ RestrictNamespaces=~cgroup net might be necessary to temporarily disable system call filters in order to simplify debugging of such failures. - If you specify both types of this option (i.e. whitelisting and blacklisting), the first encountered - will take precedence and will dictate the default action (termination or approval of a system call). Then the - next occurrences of this option will add or delete the listed system calls from the set of the filtered system - calls, depending of its type and the default action. (For example, if you have started with a whitelisting of - read and write, and right after it add a blacklisting of - write, then write will be removed from the set.) + If you specify both types of this option (i.e. allow-listing and deny-listing), the first + encountered will take precedence and will dictate the default action (termination or approval of a + system call). Then the next occurrences of this option will add or delete the listed system calls + from the set of the filtered system calls, depending of its type and the default action. (For + example, if you have started with an allow list rule for read and + write, and right after it add a deny list rule for write, + then write will be removed from the set.) As the number of possible system calls is large, predefined sets of system calls are provided. A set starts with @ character, followed by name of the set. @@ -1714,7 +1806,7 @@ RestrictNamespaces=~cgroup net @file-system - File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links. + File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links @io-event @@ -1730,7 +1822,7 @@ RestrictNamespaces=~cgroup net @memlock - Locking of memory into RAM (mlock2, mlockall2 and related calls) + Locking of memory in RAM (mlock2, mlockall2 and related calls) @module @@ -1754,7 +1846,7 @@ RestrictNamespaces=~cgroup net @process - Process control, execution, namespaceing operations (clone2, kill2, namespaces7, … + Process control, execution, namespaceing operations (clone2, kill2, namespaces7, …) @raw-io @@ -1782,11 +1874,11 @@ RestrictNamespaces=~cgroup net @sync - Synchronizing files and memory to disk: (fsync2, msync2, and related calls) + Synchronizing files and memory to disk (fsync2, msync2, and related calls) @system-service - A reasonable set of system calls used by common system services, excluding any special purpose calls. This is the recommended starting point for whitelisting system calls for system services, as it contains what is typically needed by system services, but excludes overly specific interfaces. For example, the following APIs are excluded: @clock, @mount, @swap, @reboot. + A reasonable set of system calls used by common system services, excluding any special purpose calls. This is the recommended starting point for allow-listing system calls for system services, as it contains what is typically needed by system services, but excludes overly specific interfaces. For example, the following APIs are excluded: @clock, @mount, @swap, @reboot. @timer @@ -1802,9 +1894,10 @@ RestrictNamespaces=~cgroup net systemd-analyze syscall-filter to list the actual list of system calls in each filter. - Generally, whitelisting system calls (rather than blacklisting) is the safer mode of operation. It is - recommended to enforce system call whitelists for all long-running system services. Specifically, the - following lines are a relatively safe basic choice for the majority of system services: + Generally, allow-listing system calls (rather than deny-listing) is the safer mode of + operation. It is recommended to enforce system call allow lists for all long-running system + services. Specifically, the following lines are a relatively safe basic choice for the majority of + system services: [Service] SystemCallFilter=@system-service @@ -1815,9 +1908,9 @@ SystemCallErrorNumber=EPERM call may be used to execute operations similar to what can be done with the older kill() system call, hence blocking the latter without the former only provides weak protection. Since new system calls are added regularly to the kernel as development progresses, - keeping system call blacklists comprehensive requires constant work. It is thus recommended to use - whitelisting instead, which offers the benefit that new system calls are by default implicitly - blocked until the whitelist is updated. + keeping system call deny lists comprehensive requires constant work. It is thus recommended to use + allow-listing instead, which offers the benefit that new system calls are by default implicitly + blocked until the allow list is updated. Also note that a number of system calls are required to be accessible for the dynamic linker to work. The dynamic linker is required for running most regular programs (specifically: all dynamic ELF @@ -1859,7 +1952,7 @@ SystemCallErrorNumber=EPERM manager is compiled for). If running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. setting User=nobody), NoNewPrivileges=yes is implied. By default, this option is set to the empty list, i.e. no - system call architecture filtering is applied. + filtering is applied. If this setting is used, processes of this unit will only be permitted to call native system calls, and system calls of the specified architectures. For the purposes of this option, the x32 architecture is treated @@ -2117,8 +2210,9 @@ SystemCallErrorNumber=EPERM AF_UNIX socket in the file system, as in that case only a single stream connection is created for both input and output. - is similar to above, but it opens the file in append mode. + is similar to + above, but it opens the file in append mode. + connects standard output to a socket acquired via socket activation. The semantics are similar to the same option of StandardInput=, see above. @@ -2455,7 +2549,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy UnsetEnvironment= are removed again from the compiled environment variable list, immediately before it is passed to the executed process. - The following select environment variables are set or propagated by the service manager for each invoked + The following environment variables are set or propagated by the service manager for each invoked process: @@ -2526,7 +2620,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy $LOGS_DIRECTORY $CONFIGURATION_DIRECTORY - Contains and absolute paths to the directories defined with + Absolute paths to the directories defined with RuntimeDirectory=, StateDirectory=, CacheDirectory=, LogsDirectory=, and ConfigurationDirectory= when those settings are used. @@ -2588,6 +2682,13 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + + $LOG_NAMESPACE + + If the LogNamespace= service setting is used, contains name of the + selected logging namespace. + + $JOURNAL_STREAM @@ -3125,7 +3226,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy 242 EXIT_NUMA_POLICY - Failed to set up unit's NUMA memory policy. See NUMAPolicy= and NUMAMask=above. + Failed to set up unit's NUMA memory policy. See NUMAPolicy= and NUMAMask= above. diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index dd0b59199..babbe14e0 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -310,6 +310,7 @@ find $dir systemd-rc-local-generator8, systemd-system-update-generator8, systemd-sysv-generator8, + systemd-xdg-autostart-generator8, systemd.unit5, systemctl1, systemd.environment-generator7 diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index a0771f3c1..197a468f2 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -139,6 +139,16 @@ + + + DOCUMENTATION= + + A documentation URL with further information about the topic of the log message. Tools such + as journalctl will include a hyperlink to an URL specified this way in their + output. Should be a http://, https://, + file:/, man: or info: URL. + +
@@ -347,15 +357,16 @@ _LINE_BREAK= - Only applies to _TRANSPORT=stdout records: indicates that the log message in the - standard output/error stream was not terminated with a normal newline character (\n, - i.e. ASCII 10). Specifically, when set this field is one of (in case the line was - terminated by a NUL byte), (in case the maximum log line length was reached, as - configured with LineMax= in - journald.conf5) or - (if this was the last log record of a stream and the stream ended without a final - newline character). Note that this record is not generated when a normal newline character was used for - marking the log line end. + Only applies to _TRANSPORT=stdout records: indicates that the log message + in the standard output/error stream was not terminated with a normal newline character + (\n, i.e. ASCII 10). Specifically, when set this field is one of + (in case the line was terminated by a NUL byte), (in + case the maximum log line length was reached, as configured with LineMax= in + journald.conf5), + (if this was the last log record of a stream and the stream ended without a + final newline character), or (if the process which generated the log + output changed in the middle of a line). Note that this record is not generated when a normal + newline character was used for marking the log line end. @@ -513,7 +524,8 @@ structured log entries via calls such as sd_journal_send3. They may also not be used as matches for - sd_journal_add_match3 + sd_journal_add_match3. + diff --git a/man/systemd.kill.xml b/man/systemd.kill.xml index 6a1c67d40..73f61c80e 100644 --- a/man/systemd.kill.xml +++ b/man/systemd.kill.xml @@ -61,28 +61,25 @@ KillMode= - Specifies how processes of this unit shall be - killed. One of - , - , - , + Specifies how processes of this unit shall be killed. One of + , , , . - If set to , all remaining - processes in the control group of this unit will be killed on - unit stop (for services: after the stop command is executed, - as configured with ExecStop=). If set to - , only the main process itself is - killed. If set to , the - SIGTERM signal (see below) is sent to the - main process while the subsequent SIGKILL - signal (see below) is sent to all remaining processes of the - unit's control group. If set to , no - process is killed. In this case, only the stop command will be - executed on unit stop, but no process will be killed otherwise. - Processes remaining alive after stop are left in their control - group and the control group continues to exist after stop - unless it is empty. + If set to , all remaining processes in the control group of this + unit will be killed on unit stop (for services: after the stop command is executed, as configured + with ExecStop=). If set to , the + SIGTERM signal (see below) is sent to the main process while the subsequent + SIGKILL signal (see below) is sent to all remaining processes of the unit's + control group. If set to , only the main process itself is killed (not + recommended!). If set to , no process is killed (strongly recommended + against!). In this case, only the stop command will be executed on unit stop, but no process will be + killed otherwise. Processes remaining alive after stop are left in their control group and the + control group continues to exist after stop unless empty. + + Note that it is not recommended to set KillMode= to + process or even none, as this allows processes to escape + the service manager's lifecycle and resource management, and to remain running even while their + service is considered stopped and is assumed to not consume any resources. Processes will first be terminated via SIGTERM (unless the signal to send is changed via KillSignal= or RestartKillSignal=). Optionally, @@ -154,7 +151,7 @@ terminate upon receiving the initial SIGTERM signal. This can be achieved by configuring LimitCORE= and setting FinalKillSignal= to either - SIGQUIT or SIGABRT + SIGQUIT or SIGABRT. Defaults to SIGKILL. diff --git a/man/systemd.link.xml b/man/systemd.link.xml index acda31066..af69c4838 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -27,9 +27,9 @@ Description A plain ini-style text file that encodes configuration for matching network devices, used by - systemd-udev8 and in + systemd-udevd8 and in particular its net_setup_link builtin. See - systemd.syntax5 for a + systemd.syntax7 for a general description of the syntax. The link files are read from the files located in the system @@ -64,8 +64,8 @@ [Match] Section Options A link file is said to match a device if all matches specified by the - [Match] section are satisfied. When a link file does not contain valid settings - in [Match] section, then the file will match all devices and + [Match] section are satisfied. When a link file does not contain valid settings + in [Match] section, then the file will match all devices and systemd-udevd warns about that. Hint: to avoid the warning and to make it clear that all interfaces shall be matched, add the following: OriginalName=* @@ -695,6 +695,20 @@ When unset, the kernel's default will be used. + + RxMiniBufferSize= + + Takes an integer. Specifies the maximum number of pending packets in the NIC mini receive buffer. + When unset, the kernel's default will be used. + + + + RxJumboBufferSize= + + Takes an integer. Specifies the maximum number of pending packets in the NIC jumbo receive buffer. + When unset, the kernel's default will be used. + + TxBufferSize= @@ -702,6 +716,30 @@ When unset, the kernel's default will be used. + + RxFlowControl= + + Takes a boolean. When set, enables the receive flow control, also known as the ethernet + receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's + default will be used. + + + + TxFlowControl= + + Takes a boolean. When set, enables the transmit flow control, also known as the ethernet + transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's + default will be used. + + + + AutoNegotiationFlowControl= + + Takes a boolean. When set, the auto negotiation enables the interface to exchange state + advertisements with the connected peer so that the two devices can agree on the ethernet + PAUSE configuration. When unset, the kernel's default will be used. + + diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 3122eef2a..9e1f5d40f 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -34,9 +34,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The mount specific configuration options are - configured in the [Mount] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The mount specific configuration options are + configured in the [Mount] section. Additional options are listed in systemd.exec5, @@ -367,6 +367,17 @@ Options= setting in a unit file. + + + + If a mount operation fails to mount the file system + read-write, it normally tries mounting the file system read-only instead. + This option disables that behaviour, and causes the mount to fail + immediately instead. This option is translated into the + ReadWriteOnly= setting in a unit file. + + + @@ -452,9 +463,9 @@ created. (See systemd.device5 for more information.) This option is mandatory. Note that the usual specifier expansion is applied - to this setting, literal percent characters should hence be written as %%. If this - mount is a bind mount and the specified path does not exist yet it is created as - directory. + to this setting, literal percent characters should hence be written as %%. If this mount is a bind mount and the specified path does not exist + yet it is created as directory. @@ -477,7 +488,7 @@ Mount options to use when mounting. This takes a comma-separated list of options. This setting is optional. Note that the usual specifier expansion is applied to this setting, literal percent characters - should hence be written as %%. + should hence be written as %%. @@ -505,6 +516,19 @@ off. + + ReadWriteOnly= + + Takes a boolean argument. If false, a mount + point that shall be mounted read-write but cannot be mounted + so is retried to be mounted read-only. If true the operation + will fail immediately after the read-write mount attempt did + not succeed. This corresponds with + mount8's + -w switch. Defaults to + off. + + ForceUnmount= diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml index 4fc85d40e..324c94dbd 100644 --- a/man/systemd.net-naming-scheme.xml +++ b/man/systemd.net-naming-scheme.xml @@ -357,7 +357,7 @@ Note that latest may be used to denote the latest scheme known (to this - particular version of systemd. + particular version of systemd). diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 6ad1dc9e7..c2957fd18 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -29,7 +29,7 @@ A plain ini-style text file that encodes configuration about a virtual network device, used by systemd-networkd8. - See systemd.syntax5 + See systemd.syntax7 for a general description of the syntax. The main Virtual Network Device file must have the extension .netdev; @@ -114,10 +114,10 @@ An IPv4 over IPv4 tunnel. ipvlan - An ipvlan device is a stacked device which receives packets from its underlying device based on IP address filtering. + An IPVLAN device is a stacked device which receives packets from its underlying device based on IP address filtering. ipvtap - An ipvtap device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface. + An IPVTAP device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface. macvlan A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering. @@ -171,9 +171,6 @@ wireguard WireGuard Secure Network Tunnel. - netdevsim - A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces. - nlmon A Netlink monitor device. Use an nlmon device when you want to monitor system Netlink messages. @@ -196,7 +193,7 @@ [Match] Section Options A virtual network device is only created if the - [Match] section matches the current + [Match] section matches the current environment, or if the section is empty. The following keys are accepted: @@ -262,7 +259,7 @@ [NetDev] Section Options - The [NetDev] section accepts the + The [NetDev] section accepts the following keys: @@ -276,13 +273,13 @@ Name= The interface name used when creating the netdev. - This option is compulsory. + This setting is compulsory. Kind= - The netdev kind. This option is compulsory. See the + The netdev kind. This setting is compulsory. See the Supported netdev kinds section for the valid keys. @@ -290,10 +287,10 @@ MTUBytes= - The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G, + The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G are supported and are understood to the base of 1024. For tun or tap devices, MTUBytes= setting is not currently supported in - [NetDev] section. Please specify it in [Link] section of + [NetDev] section. Please specify it in [Link] section of corresponding systemd.network5 files. @@ -303,8 +300,8 @@ MACAddress= The MAC address to use for the device. For tun or tap - devices, setting MACAddress= in the [NetDev] section is not - supported. Please specify it in [Link] section of the corresponding + devices, setting MACAddress= in the [NetDev] section is not + supported. Please specify it in [Link] section of the corresponding systemd.network5 file. If this option is not set, vlan devices inherit the MAC address of the physical interface. For other kind of netdevs, if this option is not set, then MAC address is @@ -319,7 +316,7 @@ [Bridge] Section Options - The [Bridge] section only applies for + The [Bridge] section only applies for netdevs of kind bridge, and accepts the following keys: @@ -408,6 +405,15 @@ + + VLANProtocol= + + Allows setting the protocol used for VLAN filtering. Takes + or, + , and defaults to unset and kernel's default is used. + + + STP= @@ -430,7 +436,7 @@ [VLAN] Section Options - The [VLAN] section only applies for + The [VLAN] section only applies for netdevs of kind vlan, and accepts the following key: @@ -439,7 +445,7 @@ Id= The VLAN ID to use. An integer in the range 0–4094. - This option is compulsory. + This setting is compulsory. @@ -472,8 +478,8 @@ ReorderHeader= - Takes a boolean. The VLAN reorder header is set VLAN interfaces behave like physical interfaces. - When unset, the kernel's default will be used. + Takes a boolean. When enabled, the VLAN reorder header is used and VLAN interfaces behave + like physical interfaces. When unset, the kernel's default will be used. @@ -482,7 +488,7 @@ [MACVLAN] Section Options - The [MACVLAN] section only applies for + The [MACVLAN] section only applies for netdevs of kind macvlan, and accepts the following key: @@ -493,26 +499,37 @@ The MACVLAN mode to use. The supported options are private, vepa, - bridge, and - passthru. + bridge, + passthru, and + source. + + SourceMACAddress= + + A whitespace-separated list of remote hardware addresses allowed on the MACVLAN. This + option only has an effect in source mode. Use full colon-, hyphen- or dot-delimited + hexadecimal. This option may appear more than once, in which case the lists are merged. If + the empty string is assigned to this option, the list of hardware addresses defined prior + to this is reset. Defaults to unset. + + [MACVTAP] Section Options - The [MACVTAP] section applies for + The [MACVTAP] section applies for netdevs of kind macvtap and accepts the - same key as [MACVLAN]. + same key as [MACVLAN]. [IPVLAN] Section Options - The [IPVLAN] section only applies for + The [IPVLAN] section only applies for netdevs of kind ipvlan, and accepts the following key: @@ -539,15 +556,15 @@ [IPVTAP] Section Options - The [IPVTAP] section only applies for + The [IPVTAP] section only applies for netdevs of kind ipvtap and accepts the - same key as [IPVLAN]. + same key as [IPVLAN]. [VXLAN] Section Options - The [VXLAN] section only applies for + The [VXLAN] section only applies for netdevs of kind vxlan, and accepts the following keys: @@ -573,7 +590,8 @@ Group= - Configures VXLAN multicast group IP address. All members of a VXLAN must use the same multicast group address. + Configures VXLAN multicast group IP address. All members of a VXLAN must use the same + multicast group address. @@ -631,8 +649,7 @@ L3MissNotification= - Takes a boolean. When true, enables netlink IP address miss - notifications. + Takes a boolean. When true, enables netlink IP address miss notifications. @@ -733,7 +750,7 @@ [GENEVE] Section Options - The [GENEVE] section only applies for + The [GENEVE] section only applies for netdevs of kind geneve, and accepts the following keys: @@ -759,15 +776,16 @@ TTL= - Accepts the same key in [VXLAN] section except when unset or - set to 0, the kernel's default will be used meaning that packets TTL will be set from + Accepts the same values as in the [VXLAN] section, except that when unset + or set to 0, the kernel's default will be used, meaning that packet TTL will be set from /proc/sys/net/ipv4/ip_default_ttl. UDPChecksum= - Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4. + Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets + over IPv4. @@ -798,7 +816,7 @@ IPDoNotFragment= - Accepts the same key in [VXLAN] section. + Accepts the same key in [VXLAN] section. @@ -807,7 +825,7 @@ [L2TP] Section Options - The [L2TP] section only applies for + The [L2TP] section only applies for netdevs of kind l2tp, and accepts the following keys: @@ -815,21 +833,23 @@ TunnelId= - Specifies the tunnel id. The value used must match the PeerTunnelId= value being used at the peer. - Ranges a number between 1 and 4294967295). This option is compulsory. + Specifies the tunnel identifier. Takes an number in the range 1–4294967295. The value used + must match the PeerTunnelId= value being used at the peer. This setting is + compulsory. PeerTunnelId= - Specifies the peer tunnel id. The value used must match the PeerTunnelId= value being used at the peer. - Ranges a number between 1 and 4294967295). This option is compulsory. + Specifies the peer tunnel id. Takes a number in the range 1—4294967295. The value used must + match the PeerTunnelId= value being used at the peer. This setting is + compulsory. Remote= - Specifies the IP address of the remote peer. This option is compulsory. + Specifies the IP address of the remote peer. This setting is compulsory. @@ -846,27 +866,29 @@ EncapsulationType= - Specifies the encapsulation type of the tunnel. Takes one of udp or ip. + Specifies the encapsulation type of the tunnel. Takes one of udp or + ip. UDPSourcePort= - Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected it's mandotory. Ignored when ip - encapsulation is selected. + Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected + it's mandatory. Ignored when IP encapsulation is selected. UDPDestinationPort= - Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip + Specifies destination port. When UDP encapsulation is selected it's mandatory. Ignored when IP encapsulation is selected. UDPChecksum= - Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4. + Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets + over IPv4. @@ -887,28 +909,30 @@ [L2TPSession] Section Options - The [L2TPSession] section only applies for + The [L2TPSession] section only applies for netdevs of kind l2tp, and accepts the following keys: Name= - Specifies the name of the session. This option is compulsory. + Specifies the name of the session. This setting is compulsory. SessionId= - Specifies the session id. The value used must match the SessionId= value being used at the peer. - Ranges a number between 1 and 4294967295). This option is compulsory. + Specifies the session identifier. Takes an number in the range 1–4294967295. The value used + must match the SessionId= value being used at the peer. This setting is + compulsory. PeerSessionId= - Specifies the peer session id. The value used must match the PeerSessionId= value being used at the peer. - Ranges a number between 1 and 4294967295). This option is compulsory. + Specifies the peer session identifier. Takes an number in the range 1–4294967295. + The value used must match the PeerSessionId= value being used at the peer. + This setting is compulsory. @@ -923,7 +947,7 @@ [MACsec] Section Options - The [MACsec] section only applies for network devices of kind + The [MACsec] section only applies for network devices of kind macsec, and accepts the following keys: @@ -946,7 +970,7 @@ [MACsecReceiveChannel] Section Options - The [MACsecReceiveChannel] section only applies for network devices of + The [MACsecReceiveChannel] section only applies for network devices of kind macsec, and accepts the following keys: @@ -962,7 +986,7 @@ MACAddress= Specifies the MAC address to be used for the MACsec receive channel. The MAC address - used to make secure channel identifier (SCI). This option is compulsory, and is not set by + used to make secure channel identifier (SCI). This setting is compulsory, and is not set by default. @@ -972,7 +996,7 @@ [MACsecTransmitAssociation] Section Options - The [MACsecTransmitAssociation] section only applies for network devices + The [MACsecTransmitAssociation] section only applies for network devices of kind macsec, and accepts the following keys: @@ -996,7 +1020,7 @@ Key= Specifies the encryption key used in the transmission channel. The same key must be - configured on the peer’s matching receive channel. This option is compulsory, and is not set + configured on the peer’s matching receive channel. This setting is compulsory, and is not set by default. Takes a 128-bit key encoded in a hexadecimal string, for example dffafc8d7b9a43d5b9a3dfbbf6a30c16. @@ -1004,11 +1028,13 @@ KeyFile= - Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal - string, which will be used in the transmission channel. When this option is specified, + Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal string, + which will be used in the transmission channel. When this option is specified, Key= is ignored. Note that the file must be readable by the user systemd-network, so it should be, e.g., owned by - root:systemd-network with a 0640 file mode. + root:systemd-network with a 0640 file mode. If the path + refers to an AF_UNIX stream socket in the file system a connection is made to + it and the key read from it. @@ -1022,7 +1048,7 @@ UseForEncoding= Takes a boolean. If enabled, then the security association is used for encoding. Only - one [MACsecTransmitAssociation] section can enable this option. When enabled, + one [MACsecTransmitAssociation] section can enable this option. When enabled, Activate=yes is implied. Defaults to unset. @@ -1032,7 +1058,7 @@ [MACsecReceiveAssociation] Section Options - The [MACsecReceiveAssociation] section only applies for + The [MACsecReceiveAssociation] section only applies for network devices of kind macsec, and accepts the following keys: @@ -1040,43 +1066,43 @@ Port= - Accepts the same key in [MACsecReceiveChannel] section. + Accepts the same key in [MACsecReceiveChannel] section. MACAddress= - Accepts the same key in [MACsecReceiveChannel] section. + Accepts the same key in [MACsecReceiveChannel] section. PacketNumber= - Accepts the same key in [MACsecTransmitAssociation] section. + Accepts the same key in [MACsecTransmitAssociation] section. KeyId= - Accepts the same key in [MACsecTransmitAssociation] section. + Accepts the same key in [MACsecTransmitAssociation] section. Key= - Accepts the same key in [MACsecTransmitAssociation] section. + Accepts the same key in [MACsecTransmitAssociation] section. KeyFile= - Accepts the same key in [MACsecTransmitAssociation] section. + Accepts the same key in [MACsecTransmitAssociation] section. Activate= - Accepts the same key in [MACsecTransmitAssociation] section. + Accepts the same key in [MACsecTransmitAssociation] section. @@ -1085,7 +1111,7 @@ [Tunnel] Section Options - The [Tunnel] section only applies for + The [Tunnel] section only applies for netdevs of kind ipip, sit, @@ -1130,7 +1156,7 @@ A fixed Time To Live N on tunneled packets. N is a number in the range 1–255. 0 is a special value meaning that packets inherit the TTL value. The default value for IPv4 - tunnels is: inherit. The default value for IPv6 tunnels is + tunnels is 0 (inherit). The default value for IPv6 tunnels is 64. @@ -1186,7 +1212,7 @@ both directions (InputKey= and OutputKey=). The Key= is either a number or an IPv4 address-like dotted quad. It is used as mark-configured SAD/SPD entry as part of the lookup key (both in data - and control path) in ip xfrm (framework used to implement IPsec protocol). + and control path) in IP XFRM (framework used to implement IPsec protocol). See ip-xfrm — transform configuration for details. It is only used for VTI/VTI6, GRE, GRETAP, and ERSPAN tunnels. @@ -1268,7 +1294,7 @@ Encapsulation= - Accepts the same key as in the [FooOverUDP] section. + Accepts the same key as in the [FooOverUDP] section. @@ -1308,7 +1334,7 @@ [FooOverUDP] Section Options - The [FooOverUDP] section only applies for + The [FooOverUDP] section only applies for netdevs of kind fou and accepts the following keys: @@ -1316,29 +1342,32 @@ Encapsulation= - Specifies the encapsulation mechanism used to store networking packets of various protocols inside the UDP packets. Supports the following values: + Specifies the encapsulation mechanism used to store networking packets of various protocols + inside the UDP packets. Supports the following values: - FooOverUDP provides the simplest no frills model of UDP encapsulation, it simply encapsulates - packets directly in the UDP payload. - GenericUDPEncapsulation is a generic and extensible encapsulation, it allows encapsulation of packets for any IP - protocol and optional data as part of the encapsulation. - For more detailed information see Generic UDP Encapsulation. - Defaults to FooOverUDP. + FooOverUDP provides the simplest no frills model of UDP encapsulation, it simply + encapsulates packets directly in the UDP payload. GenericUDPEncapsulation is a + generic and extensible encapsulation, it allows encapsulation of packets for any IP protocol and + optional data as part of the encapsulation. For more detailed information see Generic UDP Encapsulation. Defaults to + FooOverUDP. Port= - Specifies the port number, where the IP encapsulation packets will arrive. Please take note that the packets - will arrive with the encapsulation will be removed. Then they will be manually fed back into the network stack, and sent ahead - for delivery to the real destination. This option is mandatory. + Specifies the port number, where the IP encapsulation packets will arrive. Please take note + that the packets will arrive with the encapsulation will be removed. Then they will be manually fed + back into the network stack, and sent ahead for delivery to the real destination. This option is + mandatory. PeerPort= - Specifies the peer port number. Defaults to unset. Note that when peer port is set Peer= address is mandotory. + Specifies the peer port number. Defaults to unset. Note that when peer port is set + Peer= address is mandatory. @@ -1354,7 +1383,8 @@ Peer= - Configures peer IP address. Note that when peer address is set PeerPort= is mandotory. + Configures peer IP address. Note that when peer address is set PeerPort= + is mandatory. @@ -1369,7 +1399,7 @@ [Peer] Section Options - The [Peer] section only applies for + The [Peer] section only applies for netdevs of kind veth and accepts the following keys: @@ -1378,7 +1408,7 @@ Name= The interface name used when creating the netdev. - This option is compulsory. + This setting is compulsory. @@ -1395,7 +1425,7 @@ [VXCAN] Section Options - The [VXCAN] section only applies for + The [VXCAN] section only applies for netdevs of kind vxcan and accepts the following key: @@ -1404,7 +1434,7 @@ Peer= The peer interface name used when creating the netdev. - This option is compulsory. + This setting is compulsory. @@ -1413,7 +1443,7 @@ [Tun] Section Options - The [Tun] section only applies for + The [Tun] section only applies for netdevs of kind tun, and accepts the following keys: @@ -1463,15 +1493,15 @@ [Tap] Section Options - The [Tap] section only applies for + The [Tap] section only applies for netdevs of kind tap, and accepts the same keys - as the [Tun] section. + as the [Tun] section. [WireGuard] Section Options - The [WireGuard] section accepts the following + The [WireGuard] section accepts the following keys: @@ -1490,11 +1520,12 @@ PrivateKeyFile= - Takes an absolute path to a file which contains the Base64 encoded private key for the interface. - When this option is specified, then PrivateKey= is ignored. - Note that the file must be readable by the user systemd-network, so it - should be, e.g., owned by root:systemd-network with a - 0640 file mode. + Takes an absolute path to a file which contains the Base64 encoded private key for the + interface. When this option is specified, then PrivateKey= is ignored. Note + that the file must be readable by the user systemd-network, so it should be, + e.g., owned by root:systemd-network with a 0640 file mode. If + the path refers to an AF_UNIX stream socket in the file system a connection is + made to it and the key read from it. @@ -1518,7 +1549,7 @@ [WireGuardPeer] Section Options - The [WireGuardPeer] section accepts the following + The [WireGuardPeer] section accepts the following keys: @@ -1541,7 +1572,7 @@ already existing public-key cryptography, for post-quantum resistance. Note that because this information is secret, you may want to set - the permissions of the .netdev file to be owned by root:systemd-networkd + the permissions of the .netdev file to be owned by root:systemd-network with a 0640 file mode. @@ -1549,10 +1580,11 @@ PresharedKeyFile= Takes an absolute path to a file which contains the Base64 encoded preshared key for the - peer. When this option is specified, then PresharedKey= is ignored. - Note that the file must be readable by the user systemd-network, so it - should be, e.g., owned by root:systemd-network with a - 0640 file mode. + peer. When this option is specified, then PresharedKey= is ignored. Note that + the file must be readable by the user systemd-network, so it should be, e.g., + owned by root:systemd-network with a 0640 file mode. If the + path refers to an AF_UNIX stream socket in the file system a connection is + made to it and the key read from it. @@ -1594,7 +1626,7 @@ [Bond] Section Options - The [Bond] section accepts the following + The [Bond] section accepts the following key: @@ -1696,14 +1728,15 @@ AdActorSystemPriority= - Specifies the 802.3ad actor system priority. Ranges [1-65535]. + Specifies the 802.3ad actor system priority. Takes a number in the range 1—65535. AdUserPortKey= - Specifies the 802.3ad user defined portion of the port key. Ranges [0-1023]. + Specifies the 802.3ad user defined portion of the port key. Takes a number in the range + 0–1023. @@ -1873,7 +1906,7 @@ [Xfrm] Section Options - The [Xfrm] section accepts the following + The [Xfrm] section accepts the following keys: @@ -1896,13 +1929,12 @@ For more detail information see - - Virtual xfrm interfaces + Virtual XFRM Interfaces. [VRF] Section Options - The [VRF] section only applies for + The [VRF] section only applies for netdevs of kind vrf and accepts the following key: @@ -1910,7 +1942,7 @@ Table= - The numeric routing table identifier. This option is compulsory. + The numeric routing table identifier. This setting is compulsory. diff --git a/man/systemd.network.xml b/man/systemd.network.xml index bd19b766c..77986192d 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -31,7 +31,7 @@ A plain ini-style text file that encodes network configuration for matching network interfaces, used by systemd-networkd8. - See systemd.syntax5 + See systemd.syntax7 for a general description of the syntax. The main network file must have the extension .network; other @@ -72,21 +72,16 @@ [Match] Section Options - The network file contains a [Match] - section, which determines if a given network file may be applied - to a given device; and a [Network] section - specifying how the device should be configured. The first (in - lexical order) of the network files that matches a given device - is applied, all later files are ignored, even if they match as - well. + The network file contains a [Match] section, which determines if a given network file may be + applied to a given device; and a [Network] section specifying how the device should be configured. The + first (in lexical order) of the network files that matches a given device is applied, all later files + are ignored, even if they match as well. - A network file is said to match a network interface if all matches specified by the - [Match] section are satisfied. When a network file does not contain valid - settings in [Match] section, then the file will match all interfaces and - systemd-networkd warns about that. Hint: to avoid the warning and to make it - clear that all interfaces shall be matched, add the following: - Name=* - The following keys are accepted: + A network file is said to match a network interface if all matches specified by the [Match] + section are satisfied. When a network file does not contain valid settings in [Match] section, then the + file will match all interfaces and systemd-networkd warns about that. Hint: to avoid + the warning and to make it clear that all interfaces shall be matched, add the following: + Name=* The following keys are accepted: @@ -132,9 +127,8 @@ A whitespace-separated list of hardware address of the currently connected wireless LAN. Use full colon-, hyphen- or dot-delimited hexadecimal. See the example in - MACAddress=. This option may appear more than one, in which case the - lists are merged. If the empty string is assigned to this option, the list of BSSID defined - prior to this is reset. + MACAddress=. This option may appear more than once, in which case the + lists are merged. If the empty string is assigned to this option, the list is reset. @@ -150,7 +144,7 @@ [Link] Section Options - The [Link] section accepts the following keys: + The [Link] section accepts the following keys: @@ -205,6 +199,15 @@ controlled by other applications. + + Group= + + Link groups are similar to port ranges found in managed switches. + When network interfaces are added to a numbered group, operations on + all the interfaces from that group can be performed at once. An unsigned + integer in the range 0—4294967294. Defaults to unset. + + RequiredForOnline= @@ -228,10 +231,96 @@ + + [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 unsigned integer in the range 0..2147483646. This option is compulsory. + + + + + VLANId= + + Specifies VLAN ID of the virtual function. Takes an unsigned integer in the range 1..4095. + + + + + QualityOfService= + + Specifies quality of service of the virtual function. Takes an unsigned 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 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 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 - The [Network] section accepts the following keys: + The [Network] section accepts the following keys: @@ -260,16 +349,15 @@ specified through DHCP is not used for name resolution. See option below. - See the [DHCPv4] or [DHCPv6] section below for - further configuration options for the DHCP client support. + See the [DHCPv4] or [DHCPv6] sections below for further configuration options for the DHCP + client support. DHCPServer= Takes a boolean. If set to yes, DHCPv4 server will be started. Defaults - to no. Further settings for the DHCP - server may be set in the [DHCPServer] + to no. Further settings for the DHCP server may be set in the [DHCPServer] section described below. @@ -289,6 +377,16 @@ + + IPv6LinkLocalAddressGenerationMode= + + Specifies how IPv6 link local address is generated. Takes one of eui64, + none, stable-privacy and random. + When unset, the kernel's default will be used. Note that if LinkLocalAdressing= + not configured as ipv6 then IPv6LinkLocalAddressGenerationMode= + is ignored. + + IPv4LLRoute= @@ -389,10 +487,8 @@ DNSSEC= - Takes a boolean. or - allow-downgrade. When true, enables - DNSSEC + Takes a boolean or allow-downgrade. When true, enables + DNSSEC DNS validation support on the link. When set to allow-downgrade, compatibility with non-DNSSEC capable networks is increased, by automatically @@ -441,7 +537,7 @@ nearest-bridge, non-tpmr-bridge and customer-bridge. Defaults to false, which turns off LLDP packet emission. If not false, a short LLDP packet with information about the local system is sent out in regular intervals on the - link. The LLDP packet will contain information about the local host name, the local machine ID (as stored + link. The LLDP packet will contain information about the local hostname, the local machine ID (as stored in machine-id5) and the local interface name, as well as the pretty hostname of the system (as set in machine-info5). LLDP @@ -459,6 +555,7 @@ reception. + BindCarrier= @@ -509,7 +606,15 @@ A DNS server address, which must be in the format described in inet_pton3. - This option may be specified more than once. This setting is read by + This option may be specified more than once. Each address can optionally take a port number + separated with :, a network interface name or index separated with + %, and a Server Name Indication (SNI) separated with #. + When IPv6 address is specified with a port number, then the address must be in the square + brackets. That is, the acceptable full formats are + 111.222.333.444:9953%ifname#example.com for IPv4 and + [1111:2222::3333]:9953%ifname#example.com for IPv6. This setting can be + specified multiple times. If an empty string is assigned, then the all previous assignments + are cleared. This setting is read by systemd-resolved.service8. @@ -520,12 +625,12 @@ this link. Each item in the list should be a domain name, optionally prefixed with a tilde (~). The domains with the prefix are called "routing-only domains". The domains without the prefix are called "search domains" and are first used as search suffixes for - extending single-label host names (host names containing no dots) to become fully qualified - domain names (FQDNs). If a single-label host name is resolved on this interface, each of the + extending single-label hostnames (hostnames containing no dots) to become fully qualified + domain names (FQDNs). If a single-label hostname is resolved on this interface, each of the specified search domains are appended to it in turn, converting it into a fully qualified domain name, until one of them may be successfully resolved. - Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for host names + Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for hostnames ending in those domains (hence also single label names, if any "search domains" are listed), are routed to the DNS servers configured for this interface. The domain routing logic is particularly useful on multi-homed hosts with DNS servers serving particular private DNS zones on each interface. @@ -558,7 +663,7 @@ NTP= - An NTP server address. This option may be specified more than once. This setting is read by + An NTP server address (either an IP address, or a hostname). This option may be specified more than once. This setting is read by systemd-timesyncd.service8. @@ -621,10 +726,10 @@ trigger the start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found on the link. The default is to disable RA reception for bridge devices or when IP forwarding is enabled, and to enable it otherwise. Cannot be enabled on bond devices and when link - local adressing is disabled. + local addressing is disabled. - Further settings for the IPv6 RA support may be configured in the - [IPv6AcceptRA] section, see below. + Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA] section, see + below. Also see ip-sysctl.txt in the kernel @@ -652,11 +757,18 @@ When unset, the kernel's default will be used. + + IPv4AcceptLocal= + Takes a boolean. Accept packets with local source addresses. In combination + with suitable routing, this can be used to direct packets between two local interfaces over + the wire and have them accepted properly. When unset, the kernel's default will be used. + + IPv4ProxyARP= Takes a boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host, usually a router, answers ARP requests intended for another machine. By "faking" its identity, - the router accepts responsibility for routing packets to the "real" destination. (see RFC 1027. When unset, the kernel's default will be used. @@ -687,18 +799,15 @@ IPv6PrefixDelegation= - Whether to enable or disable Router Advertisement sending on a link. - Allowed values are static which distributes prefixes as defined in - the [IPv6PrefixDelegation] and any [IPv6Prefix] - sections, dhcpv6 which requests prefixes using a DHCPv6 client - configured for another link and any values configured in the - [IPv6PrefixDelegation] section while ignoring all static prefix - configuration sections, yes which uses both static configuration - and DHCPv6, and false which turns off IPv6 prefix delegation - altogether. Defaults to false. See the - [IPv6PrefixDelegation] and the [IPv6Prefix] - sections for more configuration options. - + Whether to enable or disable Router Advertisement sending on a link. Allowed + values are static which distributes prefixes as defined in the + [IPv6PrefixDelegation] and any [IPv6Prefix] sections, dhcpv6 which requests + prefixes using a DHCPv6 client configured for another link and any values configured in the + [IPv6PrefixDelegation] section while ignoring all static prefix configuration sections, + yes which uses both static configuration and DHCPv6, and + false which turns off IPv6 prefix delegation altogether. Defaults to + false. See the [IPv6PrefixDelegation] and the [IPv6Prefix] sections for more + configuration options. IPv6MTUBytes= @@ -808,15 +917,17 @@ ConfigureWithoutCarrier= Takes a boolean. Allows networkd to configure a specific link even if it has no carrier. - Defaults to false. + Defaults to false. If is not explicitly set, it will + default to this value. IgnoreCarrierLoss= - A boolean. Allows networkd to retain both the static and dynamic configuration of the - interface even if its carrier is lost. Defaults to false. + Takes a boolean. Allows networkd to retain both the static and dynamic configuration + of the interface even if its carrier is lost. When unset, the value specified with + is used. @@ -852,16 +963,15 @@ [Address] Section Options - An [Address] section accepts the - following keys. Specify several [Address] + An [Address] section accepts the following keys. Specify several [Address] sections to configure several addresses. Address= - As in the [Network] section. This key is mandatory. Each - [Address] section can contain one Address= setting. + As in the [Network] section. This key is mandatory. Each [Address] section can contain one + Address= setting. @@ -905,7 +1015,7 @@ Scope= The scope of the address, which can be global, - link or host or an unsigned integer ranges 0 to 255. + link or host or an unsigned integer in the range 0—255. Defaults to global. @@ -967,12 +1077,10 @@ [Neighbor] Section Options - A [Neighbor] section accepts the - following keys. The neighbor section adds a permanent, static - entry to the neighbor table (IPv6) or ARP table (IPv4) for - the given hardware address on the links matched for the network. - Specify several [Neighbor] sections to configure - several static neighbors. + A [Neighbor] section accepts the following keys. The neighbor section adds a permanent, static + entry to the neighbor table (IPv6) or ARP table (IPv4) for the given hardware address on the links + matched for the network. Specify several [Neighbor] sections to configure several static neighbors. + @@ -993,18 +1101,17 @@ [IPv6AddressLabel] Section Options - An [IPv6AddressLabel] section accepts the - following keys. Specify several [IPv6AddressLabel] - sections to configure several address labels. IPv6 address labels are - used for address selection. See RFC 3484. - Precedence is managed by userspace, and only the label itself is stored in the kernel + An [IPv6AddressLabel] section accepts the following keys. Specify several [IPv6AddressLabel] + sections to configure several address labels. IPv6 address labels are used for address selection. See + RFC 3484. Precedence is managed by userspace, + and only the label itself is stored in the kernel Label= - The label for the prefix (an unsigned integer) ranges 0 to 4294967294. - 0xffffffff is reserved. This key is mandatory. + The label for the prefix, an unsigned integer in the range 0–4294967294. + 0xffffffff is reserved. This setting is mandatory. @@ -1020,15 +1127,14 @@ [RoutingPolicyRule] Section Options - An [RoutingPolicyRule] section accepts the - following keys. Specify several [RoutingPolicyRule] + An [RoutingPolicyRule] section accepts the following keys. Specify several [RoutingPolicyRule] sections to configure several rules. TypeOfService= - Specifies the type of service to match a number between 0 to 255. + Takes a number between 0 and 255 that specifies the type of service to match. @@ -1135,16 +1241,15 @@ [NextHop] Section Options - The [NextHop] section accepts the - following keys. Specify several [NextHop] - sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop - tables. + The [NextHop] section is used to manipulate entries in the kernel's "nexthop" tables. The + [NextHop] section accepts the following keys. Specify several [NextHop] sections to configure several + hops. Gateway= - As in the [Network] section. This is mandatory. + As in the [Network] section. This is mandatory. @@ -1158,9 +1263,8 @@ [Route] Section Options - The [Route] section accepts the - following keys. Specify several [Route] - sections to configure several routes. + The [Route] section accepts the following keys. Specify several [Route] sections to configure + several routes. @@ -1207,10 +1311,10 @@ IPv6Preference= Specifies the route preference as defined in RFC4191 for Router Discovery messages. - Which can be one of low the route has a lowest priority, - medium the route has a default priority or - high the route has a highest priority. + url="https://tools.ietf.org/html/rfc4191">RFC 4191 for Router Discovery messages. Which + can be one of low the route has a lowest priority, medium + the route has a default priority or high the route has a highest priority. + @@ -1345,8 +1449,7 @@ [DHCPv4] Section Options - The [DHCPv4] section configures the - DHCPv4 client, if it is enabled with the + The [DHCPv4] section configures the DHCPv4 client, if it is enabled with the DHCP= setting described above: @@ -1373,19 +1476,19 @@ UseNTP= - When true (the default), the NTP servers received - from the DHCP server will be used by systemd-timesyncd - and take precedence over any statically configured ones. + When true (the default), the NTP servers received from the DHCP server will be used by + systemd-timesyncd.service and take precedence over any statically configured + ones. UseSIP= - When true (the default), the SIP servers received - from the DHCP server will be saved at the state files and can be - read via sd_network_link_get_sip_servers() function. + When true (the default), the SIP servers received from the DHCP server will be collected + and made available to client programs. + UseMTU= @@ -1429,6 +1532,18 @@ sent even if this is set to true. + + + MUDURL= + + When configured, the Manufacturer Usage Descriptions (MUD) URL will be sent to the + DHCPv4 server. Takes an URL of length up to 255 characters. A superficial verification that + the string is a valid URL will be performed. DHCPv4 clients are intended to have at most one + MUD URL associated with them. See + RFC 8520. + + + UseHostname= @@ -1456,7 +1571,7 @@ false. It is recommended to enable this option only on trusted networks, as setting this affects resolution - of all host names, in particular of single-label names. It is generally safer to use the supplied domain + of all hostnames, in particular of single-label names. It is generally safer to use the supplied domain only as routing domain, rather than as search domain, in order to not have it affect local resolution of single-label names. @@ -1474,7 +1589,14 @@ "link" scope will be used. For anything else, scope defaults to "global". - + + UseGateway= + + When true, the gateway will be requested from the DHCP server and added to the routing table with a + metric of 1024, and a scope of "link". When unset, the value specified with + is used. + + UseTimezone= @@ -1562,8 +1684,7 @@ RouteMetric= - Set the routing metric for routes specified by the - DHCP server. + Set the routing metric for routes specified by the DHCP server. Defaults to 1024. @@ -1573,8 +1694,8 @@ The table identifier for DHCP routes (a number between 1 and 4294967295, or 0 to unset). The table can be retrieved using ip route show table num. - When used in combination with VRF= the - VRF's routing table is used unless this parameter is specified. + When used in combination with VRF=, the + VRF's routing table is used when this parameter is not specified. @@ -1593,6 +1714,15 @@ + + FallbackLeaseLifetimeSec= + + Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime. + Takes one of forever or infinity means that the address + never expires. Defaults to unset. + + + SendRelease= @@ -1604,32 +1734,57 @@ SendDecline= - A boolen. When true, DHCPv4 clients receives IP address from DHCP server. - After new IP is received, DHCPv4 performs IPv4 Duplicate Address Detection. If duplicate use of IP is detected - the DHCPv4 client rejects the IP by sending a DHCPDECLINE packet DHCP clients try to obtain an IP address again. - See RFC 5224. - Defaults to unset. + A boolean. When true, the DHCPv4 client receives the IP address from the + DHCP server. After a new IP is received, the DHCPv4 client performs IPv4 Duplicate Address + Detection. If duplicate use is detected, the DHCPv4 client rejects the IP by sending a + DHCPDECLINE packet and tries to obtain an IP address again. See RFC 5224. Defaults to + unset. - BlackList= + DenyList= - A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected. + A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected. Note that + if AllowList= is configured then DenyList= is ignored. + + + + + AllowList= + + A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are accepted. RequestOptions= - A whitespace-separated list of integers in the range 1–254. + When configured, allows to set arbitrary request options in the DHCPv4 request options list and will be + sent to the DHCPV4 server. A whitespace-separated list of integers in the range 1..254. Defaults to unset. SendOption= - Send an arbitrary option in the DHCPv4 request. Takes a DHCP option number, data type + Send an arbitrary raw option in the DHCPv4 request. Takes a DHCP option number, data type + and data separated with a colon + (option:type:value). + The option number must be an integer in the range 1..254. The type takes one of uint8, + uint16, uint32, ipv4address, or + string. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + + + SendVendorOption= + + Send an arbitrary vendor option in the DHCPv4 request. Takes a DHCP option number, data type and data separated with a colon (option:type:value). The option number must be an integer in the range 1..254. The type takes one of uint8, @@ -1645,7 +1800,7 @@ [DHCPv6] Section Options - The [DHCPv6] section configures the DHCPv6 client, if it is enabled with the + The [DHCPv6] section configures the DHCPv6 client, if it is enabled with the DHCP= setting described above, or invoked by the IPv6 Router Advertisement: @@ -1653,7 +1808,14 @@ UseDNS= UseNTP= - As in the [DHCPv4] section. + As in the [DHCPv4] section. + + + + + RouteMetric= + + Set the routing metric for routes specified by the DHCP server. Defaults to 1024. @@ -1663,13 +1825,48 @@ Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through a rapid two-message exchange (solicit and reply). When the rapid commit option is enabled by both the DHCPv6 client and the DHCPv6 server, the two-message exchange is used, rather than the default - four-method exchange (solicit, advertise, request, and reply). The two-message exchange provides + four-message exchange (solicit, advertise, request, and reply). The two-message exchange provides faster client configuration and is beneficial in environments in which networks are under a heavy load. See RFC 3315 for details. Defaults to true. + + MUDURL= + + When configured, the Manufacturer Usage Descriptions (MUD) URL will be sent to the DHCPV6 server. + Takes an URL of length up to 255 characters. A superficial verification that the string is a valid URL + will be performed. DHCPv6 clients are intended to have at most one MUD URL associated with them. See + RFC 8520. + + + + + RequestOptions= + + When configured, allows to set arbitrary request options in the DHCPv6 request options list and will + sent to the DHCPV6 server. A whitespace-separated list of integers in the range 1..254. Defaults to unset. + + + + + SendVendorOption= + + Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP + option number, data type, and data separated with a colon (enterprise + identifier:option:type: + value). Enterprise identifier is an unsigned integer in the + range 1–4294967294. The option number must be an integer in the range 1–254. Data type takes one + of uint8, uint16, uint32, + ipv4address, ipv6address, or + string. Special characters in the data string may be escaped using C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + ForceDHCPv6PDOtherInformation= @@ -1689,19 +1886,103 @@ PrefixDelegationHint= - Takes an IPv6 address with prefix length as Address= in - the "[Network]" section. Specifies the DHCPv6 client for the requesting router to include - a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset. + Takes an IPv6 address with prefix length in the same format as the + Address= in the [Network] section. The DHCPv6 client will include a prefix + hint in the DHCPv6 solicitation sent to the server. The prefix length must be in the range + 1–128. Defaults to unset. + + + + + WithoutRA= + + Allows DHCPv6 client to start without router advertisements's managed or other address + configuration flag. Takes one of solicit or + information-request. Defaults to unset. + + + + + SendOption= + + As in the [DHCPv4] section, however because DHCPv6 uses 16-bit fields to store + option numbers, the option number is an integer in the range 1..65536. + + + + + UserClass= + + A DHCPv6 client can use User Class option to identify the type or category of user or applications + it represents. The information contained in this option is a string that represents the user class of which + the client is a member. Each class sets an identifying string of information to be used by the DHCP + service to classify clients. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Takes a whitespace-separated list of strings. Note that + currently NUL bytes are not allowed. + + + + + VendorClass= + + A DHCPv6 client can use VendorClass option to identify the vendor that + manufactured the hardware on which the client is running. The information + contained in the data area of this option is contained in one or more opaque + fields that identify details of the hardware configuration. Takes a + whitespace-separated list of strings. + + [DHCPv6PrefixDelegation] Section Options + The [DHCPv6PrefixDelegation] section configures delegated prefix assigned by DHCPv6 server. + The settings in this section are used only when IPv6PrefixDelegation= setting is + enabled, or set to dhcp6. + + + + SubnetId= + + Configure a specific subnet ID on the interface from a (previously) received prefix + delegation. You can either set "auto" (the default) or a specific subnet ID (as defined in + RFC 4291, section + 2.5.4), in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff + inclusive. This option is only effective when used together with + IPv6PrefixDelegation= and the corresponding configuration on the upstream + interface. + + + + + Assign= + + Takes a boolean. Specifies whether to add an address from the delegated prefixes which + are received from the WAN interface by the IPv6PrefixDelegation=. When + true (on LAN interfce), the EUI-64 algorithm will be used to form an interface identifier + from the delegated prefixes. Defaults to true. + + + + + Token= + + Specifies an optional address generation mode for Assign=. Takes an + IPv6 address. When set, the lower bits of the supplied address are combined with the upper + bits of a delegatad prefix received from the WAN interface by the + IPv6PrefixDelegation= prefixes to form a complete address. + + + + + [IPv6AcceptRA] Section Options - The [IPv6AcceptRA] section configures the IPv6 Router Advertisement - (RA) client, if it is enabled with the IPv6AcceptRA= setting described - above: + The [IPv6AcceptRA] section configures the IPv6 Router Advertisement (RA) client, if it is enabled + with the IPv6AcceptRA= setting described above: @@ -1726,7 +2007,7 @@ ~. Defaults to false. It is recommended to enable this option only on trusted networks, as setting this affects resolution - of all host names, in particular of single-label names. It is generally safer to use the supplied domain + of all hostnames, in particular of single-label names. It is generally safer to use the supplied domain only as routing domain, rather than as search domain, in order to not have it affect local resolution of single-label names. @@ -1762,19 +2043,26 @@ - BlackList= + DenyList= A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router advertisements in the list are ignored. + + DHCPv6Client= + + Takes a boolean, or the special value always. When true (the default), the DHCPv6 client will be started when the + RA has the managed or other information flag. If set to always, the DHCPv6 client will be started even if there is no + managed or other information flag in the RA. + + [DHCPServer] Section Options - The [DHCPServer] section contains - settings for the DHCP server, if enabled via the + The [DHCPServer] section contains settings for the DHCP server, if enabled via the DHCPServer= option described above: @@ -1818,52 +2106,37 @@ EmitDNS= DNS= - 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 or NTP server information at a - later point. DNS server propagation does not take - /etc/resolv.conf into account. 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 + 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 uplinkg 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. EmitNTP= NTP= - - Similar to the EmitDNS= and - DNS= settings described above, these - settings configure whether and what NTP server information - shall be emitted as part of the DHCP lease. The same syntax, - propagation semantics and defaults apply as for - EmitDNS= and - DNS=. - - - EmitSIP= SIP= + EmitPOP3= + POP3= + EmitSMTP= + SMTP= + EmitLPR= + LPR= - Similar to the EmitDNS= and - DNS= settings described above, these - settings configure whether and what SIP server information - shall be emitted as part of the DHCP lease. The same syntax, - propagation semantics and defaults apply as for - EmitDNS= and - DNS=. + Similar to the EmitDNS= and DNS= settings + described above, these settings configure whether and what server information for the indicate + protocol shall be emitted as part of the DHCP lease. The same syntax, propagation semantics and + defaults apply as for EmitDNS= and DNS=. @@ -1896,6 +2169,20 @@ Send a raw option with value via DHCPv4 server. Takes a DHCP option number, data type and data (option:type:value). The option number is an integer in the range 1..254. The type takes one of uint8, + uint16, uint32, ipv4address, ipv6address, or + string. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + + + SendVendorOption= + + Send a vendor option with value via DHCPv4 server. Takes a DHCP option number, data type + and data (option:type:value). + The option number is an integer in the range 1..254. The type takes one of uint8, uint16, uint32, ipv4address, or string. Special characters in the data string may be escaped using C-style @@ -1909,11 +2196,9 @@ [IPv6PrefixDelegation] Section Options - The [IPv6PrefixDelegation] section contains - settings for sending IPv6 Router Advertisements and whether to act as - a router, if enabled via the IPv6PrefixDelegation= - option described above. IPv6 network prefixes are defined with one or - more [IPv6Prefix] sections. + The [IPv6PrefixDelegation] section contains settings for sending IPv6 Router Advertisements and + whether to act as a router, if enabled via the IPv6PrefixDelegation= option described + above. IPv6 network prefixes are defined with one or more [IPv6Prefix] sections. @@ -1957,32 +2242,26 @@ EmitDNS= DNS= - DNS= specifies a list of recursive DNS server IPv6 addresses - that are distributed via Router Advertisement messages when EmitDNS= is - true. DNS= also takes special value _link_local; in that - case the IPv6 link local address is distributed. If DNS= is empty, DNS - servers are read from the [Network] section. If the - [Network] section does not contain any DNS servers either, DNS servers from - the uplink with the highest priority default route are used. When EmitDNS= - is false, no DNS server information is sent in Router Advertisement messages. - EmitDNS= defaults to true. - + DNS= specifies a list of recursive DNS server IPv6 addresses that + are distributed via Router Advertisement messages when EmitDNS= is + true. DNS= also takes special value _link_local; in that case + the IPv6 link local address is distributed. If DNS= is empty, DNS servers are read + from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS + servers from the uplink with the highest priority default route are used. When + EmitDNS= is false, no DNS server information is sent in Router Advertisement + messages. EmitDNS= defaults to true. EmitDomains= Domains= - A list of DNS search domains distributed via Router - Advertisement messages when EmitDomains= is true. If - Domains= is empty, DNS search domains are read from the - [Network] section. If the [Network] - section does not contain any DNS search domains either, DNS search - domains from the uplink with the highest priority default route are - used. When EmitDomains= is false, no DNS search domain - information is sent in Router Advertisement messages. - EmitDomains= defaults to true. - + A list of DNS search domains distributed via Router Advertisement messages when + EmitDomains= is true. If Domains= is empty, DNS search domains + are read from the [Network] section. If the [Network] section does not contain any DNS search domains + either, DNS search domains from the uplink with the highest priority default route are used. When + EmitDomains= is false, no DNS search domain information is sent in Router + Advertisement messages. EmitDomains= defaults to true. @@ -1998,10 +2277,9 @@ [IPv6Prefix] Section Options - One or more [IPv6Prefix] sections contain the IPv6 - prefixes that are announced via Router Advertisements. See - RFC 4861 - for further details. + One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router + Advertisements. See RFC 4861 for further + details. @@ -2019,13 +2297,11 @@ Prefix= - The IPv6 prefix that is to be distributed to hosts. - Similarly to configuring static IPv6 addresses, the setting is - configured as an IPv6 prefix and its prefix length, separated by a - / character. Use multiple - [IPv6Prefix] sections to configure multiple IPv6 - prefixes since prefix lifetimes, address autoconfiguration and onlink - status may differ from one prefix to another. + The IPv6 prefix that is to be distributed to hosts. Similarly to configuring static + IPv6 addresses, the setting is configured as an IPv6 prefix and its prefix length, separated by a + / character. Use multiple [IPv6Prefix] sections to configure multiple IPv6 + prefixes since prefix lifetimes, address autoconfiguration and onlink status may differ from one + prefix to another. @@ -2038,12 +2314,17 @@ to 2592000 seconds (30 days). + + Assign= + Takes a boolean. When true, adds an address from the prefix. Default to false. + + [IPv6RoutePrefix] Section Options - One or more [IPv6RoutePrefix] sections contain the IPv6 + One or more [IPv6RoutePrefix] sections contain the IPv6 prefix routes that are announced via Router Advertisements. See RFC 4191 for further details. @@ -2053,12 +2334,10 @@ Route= - The IPv6 route that is to be distributed to hosts. - Similarly to configuring static IPv6 routes, the setting is - configured as an IPv6 prefix routes and its prefix route length, - separated by a/ character. Use multiple - [IPv6PrefixRoutes] sections to configure multiple IPv6 - prefix routes. + The IPv6 route that is to be distributed to hosts. Similarly to configuring static + IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length, + separated by a / character. Use multiple [IPv6PrefixRoutes] sections to configure + multiple IPv6 prefix routes. @@ -2074,8 +2353,7 @@ [Bridge] Section Options - The [Bridge] section accepts the - following keys. + The [Bridge] section accepts the following keys: UnicastFlood= @@ -2123,10 +2401,9 @@ HairPin= - Takes a boolean. Configures whether traffic may be sent back - out of the port on which it was received. When this flag is false, and the bridge - will not forward traffic back out of the receiving port. - When unset, the kernel's default will be used. + Takes a boolean. Configures whether traffic may be sent back out of the port on which it + was received. When this flag is false, then the bridge will not forward traffic back out of the + receiving port. When unset, the kernel's default will be used. @@ -2202,17 +2479,14 @@ [BridgeFDB] Section Options - The [BridgeFDB] section manages the - forwarding database table of a port and accepts the following - keys. Specify several [BridgeFDB] sections to - configure several static MAC table entries. + The [BridgeFDB] section manages the forwarding database table of a port and accepts the following + keys. Specify several [BridgeFDB] sections to configure several static MAC table entries. MACAddress= - As in the [Network] section. This - key is mandatory. + As in the [Network] section. This key is mandatory. @@ -2253,16 +2527,38 @@ + + [LLDP] Section Options + The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following + keys. + + + MUDURL= + + Controls support for Ethernet LLDP packet's Manufacturer Usage Description (MUD). MUD is an embedded software + standard defined by the IETF that allows IoT Device makers to advertise device specifications, including the intended + communication patterns for their device when it connects to the network. The network can then use this intent to author + a context-specific access policy, so the device functions only within those parameters. Takes an URL of length up to 255 + characters. A superficial verification that the string is a valid URL + will be performed. See + RFC 8520 for details. The MUD URL received + from the LLDP packets will be saved at the state files and can be read via + sd_lldp_neighbor_get_mud_url() function. + + + + + [CAN] Section Options - The [CAN] section manages the Controller Area Network (CAN bus) and accepts the - following keys. + The [CAN] section manages the Controller Area Network (CAN bus) and accepts the + following keys: BitRate= The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can - be used here. + be used here. Takes a number in the range 1..4294967295. @@ -2272,6 +2568,29 @@ 87.5%) or permille (e.g. 875‰). + + DataBitRate= + DataSamplePoint= + + The bitrate and sample point for the data phase, if CAN-FD is used. These settings are + analogous to the BitRate= and SamplePoint= keys. + + + + FDMode= + + Takes a boolean. When yes, CAN-FD mode is enabled for the interface. + Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using + the DataBitRate= and DataSamplePoint= keys. + + + + FDNonISO= + + Takes a boolean. When yes, non-ISO CAN-FD mode is enabled for the + interface. When unset, the kernel's default will be used. + + RestartSec= @@ -2282,6 +2601,13 @@ automatic restart off. By default automatic restart is disabled. + + Termination= + + Takes a boolean. When yes, the termination resistor will be selected for + the bias network. When unset, the kernel's default will be used. + + TripleSampling= @@ -2289,12 +2615,22 @@ the value of a received bit by majority rule. When unset, the kernel's default will be used. + + ListenOnly= + + Takes a boolean. When yes, listen-only mode is enabled. When the + interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK + bit. Listen-only mode is important to debug CAN networks without interfering with the + communication or acknowledge the CAN frame. When unset, the kernel's default will be used. + + + [QDisc] Section Options - The [QDisc] section manages the traffic control queueing discipline (qdisc). + The [QDisc] section manages the traffic control queueing discipline (qdisc). @@ -2305,39 +2641,20 @@ - - Handle= - - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. - - + [NetworkEmulator] Section Options - The [NetworkEmulator] section manages the queueing discipline (qdisc) of - the network emulator. It can be used to configure the kernel packet scheduler and simulate packet - delay and loss for UDP or TCP applications, or limit the bandwidth usage of a particular service to - simulate internet connections. + The [NetworkEmulator] section manages the queueing discipline (qdisc) of the network emulator. It + can be used to configure the kernel packet scheduler and simulate packet delay and loss for UDP or TCP + applications, or limit the bandwidth usage of a particular service to simulate internet connections. + - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - - - - Handle= - - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. - - + + DelaySec= @@ -2359,7 +2676,7 @@ PacketLimit= Specifies the maximum number of packets the qdisc may hold queued at a time. - An unsigned integer ranges 0 to 4294967294. Defaults to 1000. + An unsigned integer in the range 0–4294967294. Defaults to 1000. @@ -2383,25 +2700,12 @@ [TokenBucketFilter] Section Options - The [TokenBucketFilter] section manages the queueing discipline (qdisc) of - token bucket filter (tbf). + The [TokenBucketFilter] section manages the queueing discipline (qdisc) of token bucket filter + (tbf). - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - - - - Handle= - - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. - - + + LatencySec= @@ -2412,20 +2716,20 @@ - LimitSize= + LimitBytes= Takes the number of bytes that can be queued waiting for tokens to become available. When the size is suffixed with K, M, or G, it is parsed as Kilobytes, Megabytes, or Gigabytes, - respectively, to the base of 1000. Defaults to unset. + respectively, to the base of 1024. Defaults to unset. - Burst= + BurstBytes= Specifies the size of the bucket. This is the maximum amount of bytes that tokens can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is - parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to + parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset. @@ -2444,7 +2748,7 @@ The Minimum Packet Unit (MPU) determines the minimal token usage (specified in bytes) for a packet. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, - Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to zero. + Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to zero. @@ -2461,7 +2765,7 @@ MTUBytes= Specifies the size of the peakrate bucket. When suffixed with K, M, or G, the specified - size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. + size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset. @@ -2469,26 +2773,52 @@ - [StochasticFairnessQueueing] Section Options - The [StochasticFairnessQueueing] section manages the queueing discipline - (qdisc) of stochastic fairness queueing (sfq). + [PIE] Section Options + The [PIE] section manages the queueing discipline (qdisc) of Proportional Integral + controller-Enhanced (PIE). - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - + + - Handle= + PacketLimit= - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are + dropped. An unsigned integer in the range 1–4294967294. Defaults to unset and kernel's default is used. + + + + + [StochasticFairBlue] Section Options + The [StochasticFairBlue] section manages the queueing discipline (qdisc) of stochastic fair blue + (sfb). + + + + + + + PacketLimit= + + Specifies the hard limit on the queue size in number of packets. When this limit is reached, + incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and + kernel's default is used. + + + + + + + [StochasticFairnessQueueing] Section Options + The [StochasticFairnessQueueing] section manages the queueing discipline (qdisc) of stochastic + fairness queueing (sfq). + + + + PerturbPeriodSec= @@ -2500,32 +2830,119 @@ - [ControlledDelay] Section Options - The [ControlledDelay] section manages the queueing discipline (qdisc) of - controlled delay (CoDel). + [BFIFO] Section Options + The [BFIFO] section manages the queueing discipline (qdisc) of Byte limited Packet First In First + Out (bfifo). - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - + + - Handle= + LimitBytes= - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + Specifies the hard limit on the FIFO size in bytes. The size limit (a buffer size) to prevent + it from overflowing in case it is unable to dequeue packets as quickly as it receives them. When + this limit is reached, incoming packets are dropped. When suffixed with K, M, or G, the specified + size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults + to unset and kernel's default is used. + + + + + [PFIFO] Section Options + The [PFIFO] section manages the queueing discipline (qdisc) of Packet First In First Out + (pfifo). + + + + PacketLimit= - Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are - dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used. + Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer + size) to prevent it from overflowing in case it is unable to dequeue packets as quickly as it + receives them. When this limit is reached, incoming packets are dropped. An unsigned integer in the + range 0–4294967294. Defaults to unset and kernel's default is used. + + + + + + + [PFIFOHeadDrop] Section Options + The [PFIFOHeadDrop] section manages the queueing discipline (qdisc) of Packet First In First Out + Head Drop (pfifo_head_drop). + + + + + + + PacketLimit= + + As in [PFIFO] section. + + + + + + [PFIFOFast] Section Options + The [PFIFOFast] section manages the queueing discipline (qdisc) of Packet First In First Out Fast + (pfifo_fast). + + + + + + + + + [CAKE] Section Options + The [CAKE] section manages the queueing discipline (qdisc) of Common Applications Kept Enhanced + (CAKE). + + + + + + + OverheadBytes= + + Specifies that bytes to be addeded to the size of each packet. Bytes may be negative. Takes + an integer in the range from -64 to 256. Defaults to unset and kernel's default is used. + + + + + Bandwidth= + + Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is + parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to + unset and kernel's default is used. + + + + + + + [ControlledDelay] Section Options + The [ControlledDelay] section manages the queueing discipline (qdisc) of + controlled delay (CoDel). + + + + + + + PacketLimit= + + Specifies the hard limit on the queue size in number of packets. When this limit is reached, + incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and + kernel's default is used. @@ -2564,27 +2981,132 @@ - [FairQueueingControlledDelay] Section Options - The [FairQueueingControlledDelay] section manages the queueing discipline - (qdisc) of fair queuing controlled delay (FQ-CoDel). + [DeficitRoundRobinScheduler] Section Options + The [DeficitRoundRobinScheduler] section manages the queueing discipline (qdisc) of Deficit Round + Robin Scheduler (DRR). + + + + + + + [DeficitRoundRobinSchedulerClass] Section Options + The [DeficitRoundRobinSchedulerClass] section manages the traffic control class of Deficit Round + Robin Scheduler (DRR). + + + + + - Parent= + QuantumBytes= - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. + Specifies the amount of bytes a flow is allowed to dequeue before the scheduler moves + to the next class. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, + Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to the MTU of the + interface. + + + + + + + + [EnhancedTransmissionSelection] Section Options + The [EnhancedTransmissionSelection] section manages the queueing discipline (qdisc) of Enhanced + Transmission Selection (ETS). + + + + + + + Bands= + + Specifies the number of bands. An unsigned integer in the range 1–16. This value has to be at + least large enough to cover the strict bands specified through the StrictBands= + and bandwidth-sharing bands specified in QuantumBytes=. - Handle= + StrictBands= - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. + Specifies the number of bands that should be created in strict mode. An unsigned integer in + the range 1–16. + + QuantumBytes= + + Specifies the white-space separated list of quantum used in band-sharing bands. When + suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, + respectively, to the base of 1024. This setting can be specified multiple times. If an empty + string is assigned, then the all previous assignments are cleared. + + + + + PriorityMap= + + The priority map maps the priority of a packet to a band. The argument is a white-space + separated list of numbers. The first number indicates which band the packets with priority + 0 should be put to, the second is for priority 1, and so on. There can be up to 16 numbers in + the list. If there are fewer, the default band that traffic with one of the unmentioned + priorities goes to is the last one. Each band number must be 0..255. This setting can be + specified multiple times. If an empty string is assigned, then the all previous assignments + are cleared. + + + + + + + [GenericRandomEarlyDetection] Section Options + The [GenericRandomEarlyDetection] section manages the queueing discipline (qdisc) of Generic Random + Early Detection (GRED). + + + + + + + VirtualQueues= + + Specifies the number of virtual queues. Takes a integer in the range 1-16. Defaults to unset and kernel's default is used. + + + + + DefaultVirtualQueue= + + Specifies the number of default virtual queue. This must be less than VirtualQueue=. + Defaults to unset and kernel's default is used. + + + + + GenericRIO= + + Takes a boolean. It turns on the RIO-like buffering scheme. Defaults to + unset and kernel's default is used. + + + + + + + [FairQueueingControlledDelay] Section Options + The [FairQueueingControlledDelay] section manages the queueing discipline (qdisc) of fair queuing + controlled delay (FQ-CoDel). + + + + + PacketLimit= @@ -2594,7 +3116,7 @@ - MemoryLimit= + MemoryLimitBytes= Specifies the limit on the total number of bytes that can be queued in this FQ-CoDel instance. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, @@ -2627,9 +3149,9 @@ - Quantum= + QuantumBytes= - Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan. + Specifies the number of bytes used as the "deficit" in the fair queuing algorithm timespan. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to unset and kernel's default is used. @@ -2655,25 +3177,12 @@ [FairQueueing] Section Options - The [FairQueueing] section manages the queueing discipline - (qdisc) of fair queue traffic policing (FQ). + The [FairQueueing] section manages the queueing discipline (qdisc) of fair queue traffic policing + (FQ). - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - - - - Handle= - - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. - - + + PacketLimit= @@ -2692,7 +3201,7 @@ - Quantum= + QuantumBytes= Specifies the credit per dequeue RR round, i.e. the amount of bytes a flow is allowed to dequeue at once. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, @@ -2702,7 +3211,7 @@ - InitialQuantum= + InitialQuantumBytes= Specifies the initial sending rate credit, i.e. the amount of bytes a new flow is allowed to dequeue initially. When suffixed with K, M, or G, the specified size is parsed as @@ -2757,25 +3266,12 @@ [TrivialLinkEqualizer] Section Options - The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of - trivial link equalizer (teql). + The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of trivial link + equalizer (teql). - - Parent= - - Specifies the parent Queueing Discipline (qdisc). Takes one of root, - clsact or ingress. Defaults to root. - - - - - Handle= - - Specifies the major number of unique identifier of the qdisc, known as the handle. - Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset. - - + + Id= @@ -2789,11 +3285,179 @@ + + [HierarchyTokenBucket] Section Options + The [HierarchyTokenBucket] section manages the queueing discipline (qdisc) of hierarchy token + bucket (htb). + + + + + + + DefaultClass= + + Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent + to the class. Defaults to unset. + + + + + RateToQuantum= + + Takes an unsigned integer. The DRR quantums are calculated by dividing the value + configured in Rate= by RateToQuantum=. + + + + + + + [HierarchyTokenBucketClass] Section Options + The [HierarchyTokenBucketClass] section manages the traffic control class of hierarchy token bucket + (htb). + + + + + + + Priority= + + Specifies the priority of the class. In the round-robin process, classes with the lowest + priority field are tried for packets first. + + + + + QuantumBytes= + + Specifies how many bytes to serve from leaf at once. When suffixed with K, M, or G, the + specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of + 1024. + + + + + MTUBytes= + + Specifies the maximum packet size we create. When suffixed with K, M, or G, the specified + size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. + + + + + OverheadBytes= + + Takes an unsigned integer which specifies per-packet size overhead used in rate + computations. When suffixed with K, M, or G, the specified size is parsed as Kilobytes, + Megabytes, or Gigabytes, respectively, to the base of 1024. + + + + + Rate= + + Specifies the maximum rate this class and all its children are guaranteed. When suffixed + with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively, + to the base of 1000. This setting is mandatory. + + + + + CeilRate= + + Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare. + When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, + respectively, to the base of 1000. When unset, the value specified with Rate= + is used. + + + + + BufferBytes= + + Specifies the maximum bytes burst which can be accumulated during idle period. When suffixed + with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, + to the base of 1024. + + + + + CeilBufferBytes= + + Specifies the maximum bytes burst for ceil which can be accumulated during idle period. + When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, + respectively, to the base of 1024. + + + + + + + [HeavyHitterFilter] Section Options + The [HeavyHitterFilter] section manages the queueing discipline (qdisc) of Heavy Hitter Filter + (hhf). + + + + + + + PacketLimit= + + Specifies the hard limit on the queue size in number of packets. When this limit is reached, + incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and + kernel's default is used. + + + + + + + [QuickFairQueueing] Section Options + The [QuickFairQueueing] section manages the queueing discipline (qdisc) of Quick Fair Queueing + (QFQ). + + + + + + + + + [QuickFairQueueingClass] Section Options + The [QuickFairQueueingClass] section manages the traffic control class of Quick Fair Queueing + (qfq). + + + + + + + Weight= + + Specifies the weight of the class. Takes an integer in the range 1..1023. Defaults to + unset in which case the kernel default is used. + + + + + MaxPacketBytes= + + Specifies the maximum packet size in bytes for the class. When suffixed with K, M, or G, the specified + size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. When unset, + the kernel default is used. + + + + + [BridgeVLAN] Section Options - The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts - the following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. - The VLANFiltering= option has to be enabled, see [Bridge] section in + The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the + following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The + VLANFiltering= option has to be enabled, see the [Bridge] section in systemd.netdev5. @@ -2869,7 +3533,10 @@ DHCP=ipv6 Name=enp2s0 [Network] -IPv6PrefixDelegation=dhcpv6 +IPv6PrefixDelegation=dhcpv6 + +[DHCPv6] +AssignAcquiredDelegatedPrefixAddress=yes This will enable IPv6 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. diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 27eae50b5..4b37c5dc4 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -80,7 +80,7 @@ [Exec] Section Options - Settings files may include an [Exec] + Settings files may include an [Exec] section, which carries various execution parameters: @@ -344,7 +344,7 @@ [Files] Section Options - Settings files may include a [Files] + Settings files may include a [Files] section, which carries various parameters configuring the file system of the container: @@ -405,7 +405,7 @@ Inaccessible= - Masks the specified file or directly in the container, by over-mounting it with an empty file + Masks the specified file or directory in the container, by over-mounting it with an empty file node of the same type with the most restrictive access mode. Takes a file system path as argument. This option may be used multiple times to mask multiple files or directories. This option is equivalent to the command line switch , see @@ -439,7 +439,7 @@ [Network] Section Options - Settings files may include a [Network] + Settings files may include a [Network] section, which carries various parameters configuring the network connectivity of the container: diff --git a/man/systemd.offline-updates.xml b/man/systemd.offline-updates.xml index 89c12b598..81a37f678 100644 --- a/man/systemd.offline-updates.xml +++ b/man/systemd.offline-updates.xml @@ -33,7 +33,7 @@ - The package manager prepares system updates by downloading all (RPM or DEB or + The package manager prepares system updates by downloading all (.rpm or .deb or whatever) packages to update off-line in a special directory /var/lib/system-update (or another directory of the package/upgrade manager's choice). @@ -85,8 +85,8 @@ - The upgrade scripts should exit only after the update is finished. It is expected - that the service which performs the upgrade will cause the machine to reboot after it + The update scripts should exit only after the update is finished. It is expected + that the service which performs the update will cause the machine to reboot after it is done. If the system-update.target is successfully reached, i.e. all update services have run, and the /system-update symlink still exists, it will be removed and the machine rebooted as a safety measure. diff --git a/man/systemd.path.xml b/man/systemd.path.xml index f6fe3d838..604bf494b 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -34,9 +34,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The path specific configuration options are - configured in the [Path] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The path specific configuration options are + configured in the [Path] section. For each path file, a matching unit file must exist, describing the unit to activate when the path changes. By default, diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index 079cc27f1..3ccb5c492 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -571,45 +571,46 @@ IPAddressDeny=ADDRESS[/PREFIXLENGTH]… - Turn on address range network traffic filtering for IP packets sent and received over - AF_INET and AF_INET6 sockets. Both directives take a + Turn on network traffic filtering for IP packets sent and received over + AF_INET and AF_INET6 sockets. Both directives take a space separated list of IPv4 or IPv6 addresses, each optionally suffixed with an address prefix - length in bits (separated by a / character). If the latter is omitted, the - address is considered a host address, i.e. the prefix covers the whole address (32 for IPv4, 128 - for IPv6). + length in bits after a / character. If the suffix is omitted, the address is + considered a host address, i.e. the filter covers the whole address (32 bits for IPv4, 128 bits for + IPv6). The access lists configured with this option are applied to all sockets created by processes of this unit (or in the case of socket units, associated with it). The lists are implicitly combined with any lists configured for any of the parent slice units this unit might be a member - of. By default all access lists are empty. Both ingress and egress traffic is filtered by these + of. By default both access lists are empty. Both ingress and egress traffic is filtered by these settings. In case of ingress traffic the source IP address is checked against these access lists, - in case of egress traffic the destination IP address is checked. When configured the lists are - enforced as follows: + in case of egress traffic the destination IP address is checked. The following rules are applied in + turn: - Access will be granted in case an IP packet's destination/source address matches - any entry in the IPAddressAllow= setting. + Access is granted when the checked IP address matches an entry in the + IPAddressAllow= list. - Otherwise, access will be denied in case its destination/source address matches - any entry in the IPAddressDeny= setting. + Otherwise, access is denied when the checked IP address matches an entry in the + IPAddressDeny= list. - Otherwise, access will be granted. + Otherwise, access is granted. - In order to implement a whitelisting IP firewall, it is recommended to use a - IPAddressDeny=any setting on an upper-level slice unit (such as the - root slice -.slice or the slice containing all system services + In order to implement an allow-listing IP firewall, it is recommended to use a + IPAddressDeny=any setting on an upper-level slice unit + (such as the root slice -.slice or the slice containing all system services system.slice – see - systemd.special7 for - details on these slice units), plus individual per-service IPAddressAllow= lines - permitting network access to relevant services, and only them. + systemd.special7 + for details on these slice units), plus individual per-service IPAddressAllow= + lines permitting network access to relevant services, and only them. - Note that for socket-activated services, the IP access list configured on the socket unit applies to - all sockets associated with it directly, but not to any sockets created by the ultimately activated services - for it. Conversely, the IP access list configured for the service is not applied to any sockets passed into - the service via socket activation. Thus, it is usually a good idea, to replicate the IP access lists on both - the socket and the service unit, however it often makes sense to maintain one list more open and the other - one more restricted, depending on the usecase. + Note that for socket-activated services, the IP access list configured on the socket unit + applies to all sockets associated with it directly, but not to any sockets created by the + ultimately activated services for it. Conversely, the IP access list configured for the service is + not applied to any sockets passed into the service via socket activation. Thus, it is usually a + good idea to replicate the IP access lists on both the socket and the service unit. Nevertheless, + it may make sense to maintain one list more open and the other one more restricted, depending on + the usecase. If these settings are used multiple times in the same unit the specified lists are combined. If an empty string is assigned to these settings the specific access list is reset and all previous settings undone. @@ -718,7 +719,7 @@ The device node specifier is either a path to a device node in the file system, starting with /dev/, or a string starting with either char- or block- followed by a device group name, as listed in - /proc/devices. The latter is useful to whitelist all current and future + /proc/devices. The latter is useful to allow-list all current and future devices belonging to a specific device group at once. The device group is matched according to filename globbing rules, you may hence use the * and ? wildcards. (Note that such globbing wildcards are not available for device node path @@ -732,9 +733,9 @@ all pseudo TTYs and all ALSA sound devices, respectively. char-cpu/* is a specifier matching all CPU related device groups. - Note that whitelists defined this way should only reference device groups which are + Note that allow lists defined this way should only reference device groups which are resolvable at the time the unit is started. Any device groups not resolvable then are not added to - the device whitelist. In order to work around this limitation, consider extending service units + the device allow list. In order to work around this limitation, consider extending service units with a pair of After=modprobe@xyz.service and Wants=modprobe@xyz.service lines that load the necessary kernel module implementing the device group if missing. diff --git a/man/systemd.scope.xml b/man/systemd.scope.xml index b624ac5f9..449b90b48 100644 --- a/man/systemd.scope.xml +++ b/man/systemd.scope.xml @@ -89,7 +89,7 @@ Options - Scope files may include a [Scope] + Scope files may include a [Scope] section, which carries information about the scope and the units it contains. A number of options that may be used in this section are shared with other unit types. These options are @@ -97,7 +97,7 @@ systemd.kill5 and systemd.resource-control5. - The options specific to the [Scope] section + The options specific to the [Scope] section of scope units are the following: diff --git a/man/systemd.service.xml b/man/systemd.service.xml index bc05e3dcb..4e281ec6d 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -35,9 +35,9 @@ systemd.unit5 for the common options of all unit configuration files. The common configuration items are configured in the generic - [Unit] and [Install] + [Unit] and [Install] sections. The service specific configuration options are - configured in the [Service] section. + configured in the [Service] section. Additional options are listed in systemd.exec5, @@ -138,7 +138,7 @@ Options - Service files must include a [Service] + Service files must include a [Service] section, which carries information about the service and the process it supervises. A number of options that may be used in this section are shared with other unit types. These options are @@ -147,7 +147,7 @@ systemd.kill5 and systemd.resource-control5. - The options specific to the [Service] section + The options specific to the [Service] section of service units are the following: @@ -199,7 +199,7 @@ option is used without RemainAfterExit= the service will never enter active unit state, but directly transition from activating to deactivating or dead since no process is configured that - shall run continously. In particular this means that after a service of this type ran (and which + shall run continuously. In particular this means that after a service of this type ran (and which has RemainAfterExit= not set) it will not show up as started afterwards, but as dead. @@ -464,7 +464,7 @@ of the daemon, and may be used for command lines like the following: - /bin/kill -HUP $MAINPID + ExecReload=kill -HUP $MAINPID Note however that reloading a daemon by sending a signal (as with the example line above) is usually not a good choice, @@ -473,7 +473,14 @@ other. It is strongly recommended to set ExecReload= to a command that not only triggers a configuration reload of the daemon, but also - synchronously waits for it to complete. + synchronously waits for it to complete. For example, + dbus-broker1 + uses the following: + + ExecReload=busctl call org.freedesktop.DBus \ + /org/freedesktop/DBus org.freedesktop.DBus \ + ReloadConfig + @@ -553,22 +560,18 @@ TimeoutStartSec= - Configures the time to wait for start-up. If a - daemon service does not signal start-up completion within the - configured time, the service will be considered failed and - will be shut down again. Takes a unit-less value in seconds, - or a time span value such as "5min 20s". Pass - infinity to disable the timeout logic. Defaults to - DefaultTimeoutStartSec= from the manager - configuration file, except when - Type=oneshot is used, in which case the - timeout is disabled by default (see + Configures the time to wait for start-up. If a daemon service does not signal start-up + completion within the configured time, the service will be considered failed and will be shut down again. The + precise action depends on the TimeoutStartFailureMode= option. Takes a unit-less value in + seconds, or a time span value such as "5min 20s". Pass infinity to disable the timeout logic. + Defaults to DefaultTimeoutStartSec= from the manager configuration file, except when + Type=oneshot is used, in which case the timeout is disabled by default (see systemd-system.conf5). If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause the start time to be extended beyond TimeoutStartSec=. The first receipt of this message - must occur before TimeoutStartSec= is exceeded, and once the start time has exended beyond + must occur before TimeoutStartSec= is exceeded, and once the start time has extended beyond TimeoutStartSec=, the service manager will allow the service to continue to start, provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified until the service startup status is finished by READY=1. (see @@ -581,7 +584,8 @@ This option serves two purposes. First, it configures the time to wait for each ExecStop= command. If any of them times out, subsequent ExecStop= commands are skipped and the service will be terminated by SIGTERM. If no ExecStop= - commands are specified, the service gets the SIGTERM immediately. Second, it configures the time + commands are specified, the service gets the SIGTERM immediately. This default behavior + can be changed by the TimeoutStopFailureMode= option. Second, it configures the time to wait for the service itself to stop. If it doesn't terminate in the specified time, it will be forcibly terminated by SIGKILL (see KillMode= in systemd.kill5). @@ -595,7 +599,7 @@ If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause the stop time to be extended beyond TimeoutStopSec=. The first receipt of this message - must occur before TimeoutStopSec= is exceeded, and once the stop time has exended beyond + must occur before TimeoutStopSec= is exceeded, and once the stop time has extended beyond TimeoutStopSec=, the service manager will allow the service to continue to stop, provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, or terminates itself (see sd_notify3). @@ -624,7 +628,7 @@ If a service of Type=notify handles SIGABRT itself (instead of relying on the kernel to write a core dump) it can send EXTEND_TIMEOUT_USEC=… to extended the abort time beyond TimeoutAbortSec=. The first receipt of this message - must occur before TimeoutAbortSec= is exceeded, and once the abort time has exended beyond + must occur before TimeoutAbortSec= is exceeded, and once the abort time has extended beyond TimeoutAbortSec=, the service manager will allow the service to continue to abort, provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified, or terminates itself (see sd_notify3). @@ -639,6 +643,28 @@ + + TimeoutStartFailureMode= + TimeoutStopFailureMode= + + These options configure the action that is taken in case a daemon service does not signal + start-up within its configured TimeoutStartSec=, respectively if it does not stop within + TimeoutStopSec=. Takes one of , and + . Both options default to . + + If is set the service will be gracefully terminated by sending the signal + specified in KillSignal= (defaults to SIGTERM, see + systemd.kill5). If the + service does not terminate the FinalKillSignal= is sent after + TimeoutStopSec=. If is set, WatchdogSignal= is sent + instead and TimeoutAbortSec= applies before sending FinalKillSignal=. + This setting may be used to analyze services that fail to start-up or shut-down intermittently. + By using the service is immediately terminated by sending + FinalKillSignal= without any further timeout. This setting can be used to expedite the + shutdown of failing services. + + + RuntimeMaxSec= @@ -650,7 +676,7 @@ If a service of Type=notify sends EXTEND_TIMEOUT_USEC=…, this may cause the runtime to be extended beyond RuntimeMaxSec=. The first receipt of this message - must occur before RuntimeMaxSec= is exceeded, and once the runtime has exended beyond + must occur before RuntimeMaxSec= is exceeded, and once the runtime has extended beyond RuntimeMaxSec=, the service manager will allow the service to continue to run, provided the service repeats EXTEND_TIMEOUT_USEC=… within the interval specified until the service shutdown is achieved by STOPPING=1 (or termination). (see @@ -858,7 +884,7 @@ project='man-pages'>signal7 for a list of signal names. - Note that this setting does not change the the mapping between numeric exit statuses and their + Note that this setting does not change the mapping between numeric exit statuses and their names, i.e. regardless how this setting is used 0 will still be mapped to SUCCESS (and thus typically shown as 0/SUCCESS in tool outputs) and 1 to FAILURE (and thus typically shown as 1/FAILURE), and so on. It @@ -870,7 +896,7 @@ this option will have no effect. - A service with with the <varname>SuccessExitStatus=</varname> setting + A service with the <varname>SuccessExitStatus=</varname> setting SuccessExitStatus=TEMPFAIL 250 SIGUSR1 @@ -965,7 +991,14 @@ . Conversely, if an auxiliary process of the unit sends an sd_notify() message and immediately exits, the service manager might not be able to properly attribute the message to the unit, and thus will ignore it, even if - NotifyAccess= is set for it. + NotifyAccess= is set for it. + + Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications + to units correctly, sd_notify_barrier() may be used. This call acts as a synchronization point + and ensures all notifications sent before this call have been picked up by the service manager when it returns + successfully. Use of sd_notify_barrier() is needed for clients which are not invoked by the + service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the + unit. @@ -1045,8 +1078,8 @@ manager. If set to kill and one of the service's processes is killed by the OOM killer the kernel is instructed to kill all remaining processes of the service, too. Defaults to the setting DefaultOOMPolicy= in - system.conf5 is - set to, except for services where Delegate= is turned on, where it defaults to + systemd-system.conf5 + is set to, except for services where Delegate= is turned on, where it defaults to continue. Use the OOMScoreAdjust= setting to configure whether processes of the unit @@ -1462,7 +1495,7 @@ ExecStart=/usr/sbin/simple-dbus-service WantedBy=multi-user.target For bus-activatable services, do not - include a [Install] section in the systemd + include a [Install] section in the systemd service file, but use the SystemdService= option in the corresponding DBus service file, for example (/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service): diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 7157dfa32..928c9905e 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -43,21 +43,21 @@ By default, service and scope units are placed in system.slice, virtual machines and containers registered with - systemd-machined1 + systemd-machined8 are found in machine.slice, and user sessions handled by - systemd-logind1 + systemd-logind8 in user.slice. See - systemd.special5 + systemd.special7 for more information. See systemd.unit5 for the common options of all unit configuration files. The common configuration items are configured - in the generic [Unit] and [Install] sections. The + in the generic [Unit] and [Install] sections. The slice specific configuration options are configured in - the [Slice] section. Currently, only generic resource control settings + the [Slice] section. Currently, only generic resource control settings as described in systemd.resource-control5 are allowed. diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 60ea63f74..f989b99f9 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The socket specific configuration options are - configured in the [Socket] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The socket specific configuration options are + configured in the [Socket] section. Additional options are listed in systemd.exec5, @@ -117,10 +117,9 @@ Socket units automatically gain a Before= dependency on the service units they activate. - Socket units referring to file system paths (such as AF_UNIX - sockets or FIFOs) implicitly gain Requires= and - After= dependencies on all mount units - necessary to access those paths. + Socket units referring to file system paths (such as AF_UNIX + sockets or FIFOs) implicitly gain Requires= and After= + dependencies on all mount units necessary to access those paths. Socket units using the BindToDevice= setting automatically gain a BindsTo= and @@ -300,7 +299,7 @@ url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB FunctionFS endpoints location to listen on, for implementation of USB gadget functions. This expects an - absolute file system path of functionfs mount point as the argument. + absolute file system path of FunctionFS mount point as the argument. Behavior otherwise is very similar to the ListenFIFO= directive above. Use this to open the FunctionFS endpoint ep0. When using this option, the @@ -313,9 +312,9 @@ SocketProtocol= Takes one of - or . Specifies a socket protocol - (IPPROTO_UDPLITE) UDP-Lite - (IPPROTO_SCTP) SCTP socket respectively. + or . The socket will use the UDP-Lite + (IPPROTO_UDPLITE) or SCTP + (IPPROTO_SCTP) protocol, respectively. @@ -349,16 +348,14 @@ BindToDevice= - Specifies a network interface name to bind - this socket to. If set, traffic will only be accepted from the - specified network interfaces. This controls the - SO_BINDTODEVICE socket option (see socket7 - for details). If this option is used, an implicit dependency - from this socket unit on the network interface device unit - (systemd.device5 - is created. Note that setting this parameter might result in - additional dependencies to be added to the unit (see + Specifies a network interface name to bind this socket to. If set, traffic will only + be accepted from the specified network interfaces. This controls the + SO_BINDTODEVICE socket option (see socket7 for + details). If this option is used, an implicit dependency from this socket unit on the network + interface device unit is created + (see systemd.device5). + Note that setting this parameter might result in additional dependencies to be added to the unit (see above). @@ -366,12 +363,10 @@ SocketUser= SocketGroup= - Takes a UNIX user/group name. When specified, - all AF_UNIX sockets and FIFO nodes in the file system are - owned by the specified user and group. If unset (the default), - the nodes are owned by the root user/group (if run in system - context) or the invoking user/group (if run in user context). - If only a user is specified but no group, then the group is + Takes a UNIX user/group name. When specified, all AF_UNIX + sockets and FIFO nodes in the file system are owned by the specified user and group. If unset (the + default), the nodes are owned by the root user/group (if run in system context) or the invoking + user/group (if run in user context). If only a user is specified but no group, then the group is derived from the user's default group. @@ -420,10 +415,10 @@ to work unmodified with systemd socket activation. - For IPv4 and IPv6 connections, the REMOTE_ADDR - environment variable will contain the remote IP address, and REMOTE_PORT - will contain the remote port. This is the same as the format used by CGI. - For SOCK_RAW, the port is the IP protocol. + For IPv4 and IPv6 connections, the REMOTE_ADDR environment variable will + contain the remote IP address, and REMOTE_PORT will contain the remote port. This + is the same as the format used by CGI. For SOCK_RAW, the port is the IP + protocol. @@ -456,17 +451,13 @@ KeepAlive= - Takes a boolean argument. If true, the TCP/IP - stack will send a keep alive message after 2h (depending on - the configuration of - /proc/sys/net/ipv4/tcp_keepalive_time) - for all TCP streams accepted on this socket. This controls the - SO_KEEPALIVE socket option (see - socket7 - and the TCP - Keepalive HOWTO for details.) Defaults to - . + Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message + after 2h (depending on the configuration of + /proc/sys/net/ipv4/tcp_keepalive_time) for all TCP streams accepted on this + socket. This controls the SO_KEEPALIVE socket option (see socket7 and + the TCP Keepalive + HOWTO for details.) Defaults to . @@ -483,15 +474,12 @@ KeepAliveIntervalSec= - Takes time (in seconds) as argument between - individual keepalive probes, if the socket option SO_KEEPALIVE - has been set on this socket. This controls - the TCP_KEEPINTVL socket option (see - socket7 - and the TCP - Keepalive HOWTO for details.) Defaults value is 75 - seconds. + Takes time (in seconds) as argument between individual keepalive probes, if the + socket option SO_KEEPALIVE has been set on this socket. This controls the + TCP_KEEPINTVL socket option (see socket7 and + the TCP Keepalive + HOWTO for details.) Defaults value is 75 seconds. @@ -513,17 +501,16 @@ algorithm works by combining a number of small outgoing messages, and sending them all at once. This controls the TCP_NODELAY socket option (see - tcp7 + tcp7). Defaults to . Priority= - Takes an integer argument controlling the - priority for all traffic sent from this socket. This controls - the SO_PRIORITY socket option (see - socket7 - for details.). + Takes an integer argument controlling the priority for all traffic sent from this + socket. This controls the SO_PRIORITY socket option (see socket7 for + details.). @@ -559,12 +546,12 @@ ReceiveBuffer= SendBuffer= - Takes an integer argument controlling the - receive or send buffer sizes of this socket, respectively. - This controls the SO_RCVBUF and SO_SNDBUF socket options (see - socket7 - for details.). The usual suffixes K, M, G are supported and - are understood to the base of 1024. + Takes an integer argument controlling the receive or send buffer sizes of this + socket, respectively. This controls the SO_RCVBUF and + SO_SNDBUF socket options (see socket7 for + details.). The usual suffixes K, M, G are supported and are understood to the base of + 1024. @@ -593,23 +580,20 @@ Mark= - Takes an integer value. Controls the firewall - mark of packets generated by this socket. This can be used in - the firewall logic to filter packets from this socket. This - sets the SO_MARK socket option. See - iptables8 - for details. + Takes an integer value. Controls the firewall mark of packets generated by this + socket. This can be used in the firewall logic to filter packets from this socket. This sets the + SO_MARK socket option. See iptables8 for + details. ReusePort= - Takes a boolean value. If true, allows - multiple - bind2s - to this TCP or UDP port. This controls the SO_REUSEPORT socket - option. See - socket7 - for details. + Takes a boolean value. If true, allows multiple + bind2s to this TCP + or UDP port. This controls the SO_REUSEPORT socket option. See socket7 for + details. @@ -685,37 +669,40 @@ Broadcast= - Takes a boolean value. This controls the - SO_BROADCAST socket option, which allows broadcast datagrams - to be sent from this socket. Defaults to + Takes a boolean value. This controls the SO_BROADCAST socket + option, which allows broadcast datagrams to be sent from this socket. Defaults to . PassCredentials= - Takes a boolean value. This controls the - SO_PASSCRED socket option, which allows - AF_UNIX sockets to receive the - credentials of the sending process in an ancillary message. - Defaults to . + Takes a boolean value. This controls the SO_PASSCRED socket + option, which allows AF_UNIX sockets to receive the credentials of the sending + process in an ancillary message. Defaults to . PassSecurity= - Takes a boolean value. This controls the - SO_PASSSEC socket option, which allows - AF_UNIX sockets to receive the security - context of the sending process in an ancillary message. + Takes a boolean value. This controls the SO_PASSSEC socket + option, which allows AF_UNIX sockets to receive the security context of the + sending process in an ancillary message. Defaults to . + + + + PassPacketInfo= + Takes a boolean value. This controls the IP_PKTINFO, + IPV6_RECVPKTINFO and NETLINK_PKTINFO socket options, which + enable reception of additional per-packet metadata as ancillary message, on + AF_INET, AF_INET6 and AF_UNIX sockets. Defaults to . TCPCongestion= - Takes a string value. Controls the TCP - congestion algorithm used by this socket. Should be one of - "westwood", "veno", "cubic", "lp" or any other available - algorithm supported by the IP stack. This setting applies only - to stream sockets. + Takes a string value. Controls the TCP congestion algorithm used by this + socket. Should be one of westwood, veno, + cubic, lp or any other available algorithm supported by the IP + stack. This setting applies only to stream sockets. @@ -779,15 +766,12 @@ RemoveOnStop= - Takes a boolean argument. If enabled, any file - nodes created by this socket unit are removed when it is - stopped. This applies to AF_UNIX sockets in the file system, - POSIX message queues, FIFOs, as well as any symlinks to them - configured with Symlinks=. Normally, it - should not be necessary to use this option, and is not - recommended as services might continue to run after the socket - unit has been terminated and it should still be possible to - communicate with them via their file system node. Defaults to + Takes a boolean argument. If enabled, any file nodes created by this socket unit are + removed when it is stopped. This applies to AF_UNIX sockets in the file system, + POSIX message queues, FIFOs, as well as any symlinks to them configured with + Symlinks=. Normally, it should not be necessary to use this option, and is not + recommended as services might continue to run after the socket unit has been terminated and it should + still be possible to communicate with them via their file system node. Defaults to off. diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 30ec7782d..a948969a8 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -106,7 +106,7 @@ - Units managed by the system's service manager + Units managed by the system service manager Special System Units @@ -297,7 +297,7 @@ this unit (or multi-user.target) during installation. This is best configured via WantedBy=graphical.target in the unit's - [Install] section. + [Install] section. @@ -447,7 +447,7 @@ add Wants= dependencies for their unit to this unit during installation. This is best configured via WantedBy=multi-user.target in the unit's - [Install] section. + [Install] section. @@ -505,7 +505,7 @@ applications get pulled in via Wants= dependencies from this unit. This is best configured via a WantedBy=paths.target in the path unit's - [Install] section. + [Install] section. @@ -641,7 +641,7 @@ Adding slice units to slices.target is generally not necessary. Instead, when some unit that uses Slice= is started, the specified slice will be started automatically. Adding - WantedBy=slices.target lines to the [Install] + WantedBy=slices.target lines to the [Install] section should only be done for units that need to be always active. In that case care needs to be taken to avoid creating a loop through the automatic dependencies on "parent" slices. @@ -659,7 +659,7 @@ Wants= dependencies to this unit for their socket unit during installation. This is best configured via a WantedBy=sockets.target - in the socket unit's [Install] + in the socket unit's [Install] section. @@ -742,7 +742,7 @@ applications get pulled in via Wants= dependencies from this unit. This is best configured via WantedBy=timers.target in the timer - unit's [Install] section. + unit's [Install] section. @@ -1043,7 +1043,7 @@ By default, all user processes and services started on behalf of the user, including the per-user systemd instance are found in this slice. This is pulled in by - systemd-logind.service + systemd-logind.service. @@ -1052,8 +1052,8 @@ By default, all virtual machines and containers registered with systemd-machined are - found in this slice. This is pulled in by - systemd-machined.service + found in this slice. This is pulled in by + systemd-machined.service. @@ -1061,7 +1061,7 @@ - Units managed by the user's service manager + Units managed by the user service manager Special User Units @@ -1104,7 +1104,7 @@ This target is active whenever any graphical session is running. It is used to stop user services which only apply to a graphical (X, Wayland, etc.) session when the session is terminated. Such services should have - PartOf=graphical-session.target in their [Unit] + PartOf=graphical-session.target in their [Unit] section. A target for a particular session (e. g. gnome-session.target) starts and stops graphical-session.target with @@ -1153,6 +1153,18 @@ gnome-session.target. + + + xdg-desktop-autostart.target + + The XDG specification defines a way to autostart applications using XDG desktop files. + systemd ships + systemd-xdg-autostart-generator8 + for the XDG desktop files in autostart directories. + Desktop Environments can opt-in to use this service by adding a Wants= + dependency on xdg-desktop-autostart.target. + + diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index 190fc388c..4b1f850af 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -37,9 +37,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The swap specific configuration options are - configured in the [Swap] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The swap specific configuration options are + configured in the [Swap] section. Additional options are listed in systemd.exec5, @@ -166,7 +166,7 @@ Options - Swap files must include a [Swap] section, which carries + Swap unit files must include a [Swap] section, which carries information about the swap device it supervises. A number of options that may be used in this section are shared with other unit types. These options are documented in @@ -184,11 +184,13 @@ project='man-pages'>swapon8 for details. If this refers to a device node, a dependency on the respective device unit is automatically created. (See - systemd.device5 for more - information.) If this refers to a file, a dependency on the respective mount unit is automatically - created. (See systemd.mount5 - for more information.) This option is mandatory. Note that the usual specifier expansion is applied to this - setting, literal percent characters should hence be written as %%. + systemd.device5 + for more information.) If this refers to a file, a dependency on the respective mount unit is + automatically created. (See + systemd.mount5 for + more information.) This option is mandatory. Note that the usual specifier expansion is applied to + this setting, literal percent characters should hence be written as + %%. diff --git a/man/systemd.syntax.xml b/man/systemd.syntax.xml index 04b1564b1..df100ec4e 100644 --- a/man/systemd.syntax.xml +++ b/man/systemd.syntax.xml @@ -98,10 +98,10 @@ KeyTwo=value 2 \ value 2 continued [Section C] -KeyThree=value 2\ +KeyThree=value 3\ # this line is ignored ; this line is ignored too - value 2 continued + value 3 continued Boolean arguments used in configuration files can be written in diff --git a/man/systemd.target.xml b/man/systemd.target.xml index 3052b1778..a706a4588 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -34,8 +34,8 @@ This unit type has no specific options. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. A separate [Target] section does not exist, + configuration items are configured in the generic [Unit] and + [Install] sections. A separate [Target] section does not exist, since no target-specific options may be configured. Target units do not offer any additional functionality on diff --git a/man/systemd.time.xml b/man/systemd.time.xml index b4656b05d..5b7800e78 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -98,18 +98,20 @@ Parsing Timestamps - When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given - as the literal string UTC (for the UTC timezone), or is specified to be the locally configured - timezone, or the timezone name in the IANA timezone database format. The complete list of timezones - supported on your system can be obtained using the timedatectl list-timezones - (see timedatectl1). - Using IANA format is recommended over local timezone names, as less prone to errors (eg: with local timezone it's possible to - specify daylight saving time in winter, while it's incorrect). The weekday specification is optional, but when - the weekday is specified, it must either be in the abbreviated (Wed) or non-abbreviated - (Wednesday) English language form (case does not matter), and is not subject to the locale - choice of the user. Either the date, or the time part may be omitted, in which case the current date or 00:00:00, - respectively, is assumed. The seconds component of the time may also be omitted, in which case ":00" is - assumed. Year numbers may be specified in full or may be abbreviated (omitting the century). + When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless + it is given as the literal string UTC (for the UTC timezone), or is specified to be + the locally configured timezone, or the timezone name in the IANA timezone database format. The complete + list of timezones supported on your system can be obtained using the timedatectl + list-timezones (see + timedatectl1). Using + IANA format is recommended over local timezone names, as less prone to errors (e.g. with local timezone + it's possible to specify daylight saving time in winter, even though that is not correct). The weekday + specification is optional, but when the weekday is specified, it must either be in the abbreviated + (Wed) or non-abbreviated (Wednesday) English language form (case + does not matter), and is not subject to the locale choice of the user. Either the date, or the time part + may be omitted, in which case the current date or 00:00:00, respectively, is assumed. The seconds + component of the time may also be omitted, in which case ":00" is assumed. Year numbers may be specified + in full or may be abbreviated (omitting the century). A timestamp is considered invalid if a weekday is specified and the date does not match the specified day of the week. @@ -282,7 +284,7 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 Use the calendar command of systemd-analyze1 to validate and normalize calendar time specifications for testing purposes. The tool also calculates when a specified - calendar event would elapse next. + calendar event would occur next. diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 040b8e289..582240271 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 for the common options of all unit configuration files. The common - configuration items are configured in the generic [Unit] and - [Install] sections. The timer specific configuration options are - configured in the [Timer] section. + configuration items are configured in the generic [Unit] and + [Install] sections. The timer specific configuration options are + configured in the [Timer] section. For each timer file, a matching unit file must exist, describing the unit to activate when the timer elapses. By diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 86185e48a..b3deb2895 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -6,7 +6,8 @@ ]> - + systemd.unit @@ -80,7 +81,7 @@ target, a watched file system path, a timer controlled and supervised by systemd1, a resource management slice or a group of externally created processes. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. This man page lists the common configuration options of all @@ -225,9 +226,6 @@ foo-.service.d/10-override.conf would override service.d/10-override.conf. - - Note that while systemd offers a flexible dependency system between units it is recommended to use this functionality only sparingly and instead rely on techniques such as bus-based or @@ -739,7 +737,7 @@ that the listed unit is fully started up before the configured unit is started. When two units with an ordering dependency between them are shut down, the inverse of the - start-up order is applied. i.e. if a unit is configured with After= on another + start-up order is applied. I.e. if a unit is configured with After= on another unit, the former is stopped before the latter if both are shut down. Given two units with any ordering dependency between them, if one unit is shut down and the other is started up, the shutdown is ordered before the start-up. It doesn't matter if the ordering dependency is @@ -750,7 +748,8 @@ type when precisely a unit has finished starting up. Most importantly, for service units start-up is considered completed for the purpose of Before=/After= when all its configured start-up commands have been invoked and they either failed or reported start-up - success. Note that this does includes ExecStartPost (or ExecStopPost for the shutdown case). + success. Note that this does includes ExecStartPost= (or + ExecStopPost= for the shutdown case). Note that those settings are independent of and orthogonal to the requirement dependencies as configured by Requires=, Wants=, Requisite=, @@ -834,7 +833,7 @@ option for details on the possible values. If this is set to isolate, only a single unit may be listed in - OnFailure=.. + OnFailure=. @@ -1028,9 +1027,9 @@ StartLimitAction= Configure an additional action to take if the rate limit configured with - StartLimitIntervalSec= and StartLimitBurst= is hit. Takes the same - values as the setting FailureAction=/SuccessAction= settings and executes - the same actions. If is set, hitting the rate limit will trigger no action besides that + StartLimitIntervalSec= and StartLimitBurst= is hit. Takes the same + values as the FailureAction=/SuccessAction= settings. If + is set, hitting the rate limit will trigger no action except that the start will not be permitted. Defaults to . @@ -1169,6 +1168,7 @@ podman, rkt, wsl, + proot, acrn to test against a specific implementation, or private-users to check whether we are running in a user namespace. See @@ -1223,6 +1223,24 @@ + + ConditionEnvironment= + + ConditionEnvironment= may be used to check whether a specific + environment variable is set (or if prefixed with the exclamation mark — unset) in the service + manager's environment block. + + The argument may be a single word, to check if the variable with this name is defined in the + environment block, or an assignment + (name=value), to check if + the variable with this exact value is defined. Note that the environment block of the service + manager itself is checked, i.e. not any variables defined with Environment= or + EnvironmentFile=, as described above. This is particularly useful when the + service manager runs inside a containerized environment or as per-user service manager, in order to + check for variables passed in by the enclosing container manager or PAM. + + + ConditionSecurity= @@ -1273,6 +1291,13 @@ systemd-update-done.service8, to make sure they run before the stamp file's modification time gets reset indicating a completed update. + + If the systemd.condition-needs-update= option is specified on the kernel + command line (taking a boolean), it will override the result of this condition check, taking + precedence over any file modification time checks. If it is used + systemd-update-done.service will not have immediate effect on any following + ConditionNeedsUpdate= checks, until the system is rebooted where the kernel + command line option is not specified anymore. @@ -1284,6 +1309,10 @@ (specifically: an /etc with no /etc/machine-id). This may be used to populate /etc on the first boot after factory reset, or when a new system instance boots up for the first time. + + If the systemd.condition-first-boot= option is specified on the kernel + command line (taking a boolean), it will override the result of this condition check, taking + precedence over /etc/machine-id existence checks. @@ -1343,6 +1372,18 @@ + + ConditionPathIsEncrypted= + + ConditionPathIsEncrypted= is similar to + ConditionPathExists= but verifies that the underlying file system's backing + block device is encrypted using dm-crypt/LUKS. Note that this check does not cover ext4 + per-directory encryption, and only detects block level encryption. Moreover, if the specified path + resides on a file system on top of a loopback block device, only encryption above the loopback device is + detected. It is not detected whether the file system backing the loopback block device is encrypted. + + + ConditionDirectoryNotEmpty= @@ -1598,7 +1639,7 @@ [Install] Section Options - Unit files may include an [Install] section, which carries installation information for + Unit files may include an [Install] section, which carries installation information for the unit. This section is not interpreted by systemd1 during runtime; it is used by the enable and disable commands of the @@ -1692,7 +1733,7 @@ and resolvable for the setting to be valid. The following specifiers are understood: - +
Specifiers available in unit files @@ -1707,10 +1748,14 @@ - %b - Boot ID - The boot ID of the running system, formatted as string. See random4 for more information. + + %a + Architecture + A short string identifying the architecture of the local system. A string such as x86, x86-64 or arm64. See the architectures defined for ConditionArchitecture= above for a full list. + + %C Cache directory root @@ -1734,10 +1779,17 @@ Note that this setting is not influenced by the User= setting configurable in the [Service] section of the service unit. + %H Host name The hostname of the running system at the point in time the unit configuration is loaded. + + %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. + %i Instance name @@ -1763,11 +1815,8 @@ Note that this setting is not influenced by the Us Log directory root This is either /var/log (for the system manager) or the path $XDG_CONFIG_HOME resolves to with /log appended (for user managers). - - %m - Machine ID - The machine ID of the running system, formatted as string. See machine-id5 for more information. - + + %n Full unit name @@ -1832,21 +1881,15 @@ Note that this setting is not influenced by the Us Note that this setting is not influenced by the User= setting configurable in the [Service] section of the service unit. - - %v - Kernel release - Identical to uname -r output - + %V Directory for larger and persistent temporary files This is either /var/tmp or the path $TMPDIR, $TEMP or $TMP are set to. - - %% - Single percent sign - Use %% in place of % to specify a single percent sign. - + + +
diff --git a/man/systemd.xml b/man/systemd.xml index 28bf49e13..a9040545c 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -194,7 +194,7 @@ It is in an active, activating, deactivating or failed state (i.e. in any unit state except for inactive) It has a job queued for it - It is a dependency of some sort of at least one other unit that is loaded into memory + It is a dependency of at least one other unit that is loaded into memory It has some form of resource still allocated (e.g. a service unit that is inactive but for which a process is still lingering that ignored the request to be terminated) It has been pinned into memory programmatically by a D-Bus call @@ -257,7 +257,7 @@ execution compared to the target unit's state and is marked successful and complete when both satisfy. However, this job also pulls in other dependencies due to the defined relationships and thus leads to, in our - our example, start jobs for any of those inactive units getting queued as + example, start jobs for any of those inactive units getting queued as well.
systemd contains native implementations of various tasks @@ -280,6 +280,12 @@ files or parameters passed on the kernel command line. For details, see systemd.generator7. + The D-Bus API of systemd is described in + org.freedesktop.systemd15 + and + org.freedesktop.LogControl15. + + Systems which invoke systemd in a container or initrd environment should implement the Container Interface or initrd Interface @@ -606,6 +612,13 @@ Environment + + $SYSTEMD_LOG_COLOR + Controls whether systemd highlights important + log messages. This can be overridden with + . + + $SYSTEMD_LOG_LEVEL systemd reads the log level from this @@ -613,6 +626,13 @@ . + + $SYSTEMD_LOG_LOCATION + Controls whether systemd prints the code + location along with log messages. This can be overridden with + . + + $SYSTEMD_LOG_TARGET systemd reads the log target from this @@ -621,17 +641,10 @@ - $SYSTEMD_LOG_COLOR - Controls whether systemd highlights important - log messages. This can be overridden with - . - - - - $SYSTEMD_LOG_LOCATION - Controls whether systemd prints the code - location along with log messages. This can be overridden with - . + $SYSTEMD_LOG_TIME + Controls whether systemd prefixes log + messages with the current time. This can be overridden with + . @@ -649,9 +662,17 @@ $SYSTEMD_UNIT_PATH + $SYSTEMD_GENERATOR_PATH + $SYSTEMD_ENVIRONMENT_GENERATOR_PATH - Controls where systemd looks for unit - files. + Controls where systemd looks for unit files and + generators. + These variables may contain a list of paths, separated by colons + (:). When set, if the list ends with an empty + component (...:), this list is prepended to the + usual set of of paths. Otherwise, the specified list replaces the usual + set of paths. + @@ -739,13 +760,11 @@ systemd.crash_chvt - Takes a positive integer, or a boolean argument. Can be also - specified without an argument, with the same effect as a positive boolean. If - a positive integer (in the range 1–63) is specified, the system manager (PID - 1) will activate the specified virtual terminal (VT) when it - crashes. Defaults to disabled, meaning that no such switch is attempted. If - set to enabled, the VT the kernel messages are written to is selected. - + Takes a positive integer, or a boolean argument. Can be also specified without an + argument, with the same effect as a positive boolean. If a positive integer (in the range 1–63) is + specified, the system manager (PID 1) will activate the specified virtual terminal when it crashes. + Defaults to disabled, meaning that no such switch is attempted. If set to enabled, the virtual + terminal the kernel messages are written to is used instead. @@ -821,18 +840,21 @@ - systemd.log_target= - systemd.log_level= - systemd.log_location= systemd.log_color + systemd.log_level= + systemd.log_location + systemd.log_target= + systemd.log_time Controls log output, with the same effect as the - $SYSTEMD_LOG_TARGET, + $SYSTEMD_LOG_COLOR, $SYSTEMD_LOG_LEVEL, $SYSTEMD_LOG_LOCATION, - $SYSTEMD_LOG_COLOR environment variables described above. - systemd.log_color can be specified without an argument, - with the same effect as a positive boolean. + $SYSTEMD_LOG_TARGET, + $SYSTEMD_LOG_TIME, environment variables described above. + systemd.log_color, systemd.log_location, and + systemd.log_time can be specified without an argument, with the + same effect as a positive boolean.
@@ -1065,14 +1087,15 @@ this context, because they are properly namespaced. When an option is specified both on the kernel command line, and as a normal command line argument, the latter has higher precedence. - When systemd is used a user manager, the kernel command line is ignored and + When systemd is used as a user manager, the kernel command line is ignored and the options described are understood. Nevertheless, systemd is usually started in this mode through the user@.service5 service, which is shared between all users, and it may be more convenient to use configuration files to modify settings, see systemd-user.conf5, - or a drop-in that specifies one of the environment variables listed above in "Environment, see + or a drop-in that specifies one of the environment variables listed above in the Environment section, + see systemd.unit5. @@ -1122,20 +1145,8 @@ - Show terse unit status information is shown on the console during boot-up and - shutdown. See systemd.show_status above. - - - - - - Set log target. See systemd.log_target above. - - - - - - Set log level. See systemd.log_level above. + Show terse unit status information on the console during boot-up and shutdown. See + systemd.show_status above. @@ -1145,6 +1156,12 @@ + + + + Set log level. See systemd.log_level above. + + @@ -1152,6 +1169,19 @@ above. + + + + Set log target. See systemd.log_target above. + + + + + + Prefix messages with timestamp. See systemd.log_time above. + + + @@ -1227,8 +1257,9 @@ systemd-notify1, daemon7, sd-daemon3, + org.freedesktop.systemd15, systemd.unit5, - systemd.special5, + systemd.special7, pkg-config1, kernel-command-line7, bootup7, diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index f0e0f2bed..38a95d6e1 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -234,61 +234,49 @@ r - 500-900 Specifiers - Specifiers can be used in the "Name", "ID", "GECOS", "Home directory", and "Shell" fields. - An unknown or unresolvable specifier is treated as invalid configuration. - The following expansions are understood: - - Specifiers available - - - - - - - Specifier - Meaning - Details - - - - - %b - Boot ID - The boot ID of the running system, formatted as string. See random4 for more information. - - - %H - Host name - The hostname of the running system. - - - %m - Machine ID - The machine ID of the running system, formatted as string. See machine-id5 for more information. - - - %T - Directory for temporary files - This is either /tmp or the path $TMPDIR, $TEMP or $TMP are set to. - - - %v - Kernel release - Identical to uname -r output. - - - %V - Directory for larger and persistent temporary files - This is either /var/tmp or the path $TMPDIR, $TEMP or $TMP are set to. - - - %% - Escaped % - Single percent sign. - - - -
+ Specifiers can be used in the Name, ID, + GECOS, Home directory, and Shell fields. An + unknown or unresolvable specifier is treated as invalid configuration. The following expansions are + understood: + + + Specifiers available + + + + + + + Specifier + Meaning + Details + + + + + + + + + + + + %T + Directory for temporary files + This is either /tmp or the path $TMPDIR, $TEMP or $TMP are set to. + + + + %V + Directory for larger and persistent temporary files + This is either /var/tmp or the path $TMPDIR, $TEMP or $TMP are set to. + + + + + + +
diff --git a/man/tc.xml b/man/tc.xml new file mode 100644 index 000000000..f312ac269 --- /dev/null +++ b/man/tc.xml @@ -0,0 +1,48 @@ + + + + + + + + + Parent= + + Configures the parent Queueing Discipline (qdisc). Takes one of root, + clsact, ingress or a class identifier. The class identifier is + specified as the major and minor numbers in hexadecimal in the range 0x1–Oxffff separated with a + colon (major:minor). Defaults to root. + + + + + Handle= + + Configures the major number of unique identifier of the qdisc, known as the handle. + Takes a hexadecimal number in the range 0x1–0xffff. Defaults to unset. + + + + + Parent= + + Configures the parent Queueing Discipline (qdisc). Takes one of root, or a + qdisc identifier. The qdisc identifier is specified as the major and minor numbers in hexadecimal in + the range 0x1–Oxffff separated with a colon (major:minor). Defaults to + root. + + + + + + ClassId= + + Configues the unique identifier of the class. It is specified as the major and minor numbers in + hexadecimal in the range 0x1–Oxffff separated with a colon (major:minor). + Defaults to unset. + + + + diff --git a/man/timesyncd.conf.xml b/man/timesyncd.conf.xml index c64d01540..c1a37ec83 100644 --- a/man/timesyncd.conf.xml +++ b/man/timesyncd.conf.xml @@ -32,7 +32,7 @@ Description These configuration files control NTP network time synchronization. See - systemd.syntax5 + systemd.syntax7 for a general description of the syntax. @@ -41,7 +41,7 @@ Options - The following settings are configured in the [Time] section: + The following settings are configured in the [Time] section: diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 426df6cb4..b9e9eee96 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -6,7 +6,8 @@ Copyright © 2010 Brandon Philips --> - + tmpfiles.d @@ -616,7 +617,7 @@ w- /proc/sys/vm/swappiness - - - - 10 Specifiers can be used in the "path" and "argument" fields. An unknown or unresolvable specifier is treated as invalid configuration. The following expansions are understood: - +
Specifiers available @@ -630,11 +631,9 @@ w- /proc/sys/vm/swappiness - - - - 10 - - %b - Boot ID - The boot ID of the running system, formatted as string. See random4 for more information. - + + + %C System or user cache directory @@ -645,21 +644,15 @@ w- /proc/sys/vm/swappiness - - - - 10 User home directory This is the home directory of the user running the command. In case of the system instance this resolves to /root. - - %H - Host name - The hostname of the running system. - + + %L System or user log directory In mode, this is the same as $XDG_CONFIG_HOME with /log appended, and /var/log otherwise. - - %m - Machine ID - The machine ID of the running system, formatted as string. See machine-id5 for more information. - + + %S System or user state directory @@ -695,21 +688,15 @@ w- /proc/sys/vm/swappiness - - - - 10 User UID This is the numeric UID of the user running the command. In case of the system instance this resolves to 0. - - %v - Kernel release - Identical to uname -r output. - + %V Directory for larger and persistent temporary files This is either /var/tmp or the path $TMPDIR, $TEMP or $TMP are set to. - - %% - Escaped % - Single percent sign. - + + +
diff --git a/man/udev.conf.xml b/man/udev.conf.xml index 04301c81a..0b5669b44 100644 --- a/man/udev.conf.xml +++ b/man/udev.conf.xml @@ -97,6 +97,16 @@ This is the same as the option.
+ + + timeout_signal= + + + Specifies a signal that systemd-udevd will send on worker + timeouts. Note that both workers and spawned processes will be killed using this + signal. Defaults to . + + diff --git a/man/user@.service.xml b/man/user@.service.xml index f4603df07..b9d146140 100644 --- a/man/user@.service.xml +++ b/man/user@.service.xml @@ -17,34 +17,37 @@ user@.service user-runtime-dir@.service - System units to manage user processes + systemd-user-runtime-dir + System units to start the user manager user@UID.service user-runtime-dir@UID.service + /usr/lib/systemd/systemd-user-runtime-dir user-UID.slice Description - The - systemd1 + The systemd1 system manager (PID 1) starts user manager instances as - user@UID.service, where the user's numerical UID - is used as the instance identifier. Each systemd --user instance manages a - hierarchy of its own units. See - systemd1 for - a discussion of systemd units and - systemd.special1 - for a list of units that form the basis of the unit hierarchies of system and user units. + user@UID.service, with the user's numerical UID used as + the instance identifier. These instances use the same executable as the system manager, but running in a + mode where it starts a different set of units. Each systemd --user instance manages a + hierarchy of units specific to that user. See + systemd1 for a + discussion of units and + systemd.special7 for a + list of units that form the basis of the unit hierarchies of system and user units. user@UID.service is accompanied by the system unit user-runtime-dir@UID.service, which creates the user's runtime directory /run/user/UID, and then removes it when this - unit is stopped. + unit is stopped. user-runtime-dir@UID.service + executes the systemd-user-runtime-dir binary to do the actual work. User processes may be started by the user@.service instance, in which case they will be part of that unit in the system hierarchy. They may also be started elsewhere, @@ -53,11 +56,11 @@ display manager like gdm, in which case they form a .scope unit (see systemd.scope5). Both user@UID.service and the scope units are - collected under a user-UID.slice. + collected under the user-UID.slice. Individual user-UID.slice slices are collected under user.slice, see - systemd.special8. + systemd.special7. diff --git a/man/userdbctl.xml b/man/userdbctl.xml index 606ce673b..9a69f33ed 100644 --- a/man/userdbctl.xml +++ b/man/userdbctl.xml @@ -163,11 +163,10 @@ Well-Known Services The userdbctl services command will list all currently running services that - provide user or group definitions to the system. The following are well-known services are shown among - this list. + provide user or group definitions to the system. The following well-known services are shown among + this list: - io.systemd.DynamicUser @@ -187,6 +186,15 @@ available to the system. + + io.systemd.Machine + + This service is provided by + systemd-machined.service8 + and synthesizes records for all users/groups used by a container that employs user + namespacing. + + io.systemd.Multiplexer @@ -218,7 +226,7 @@ Note that userdbctl has internal support for NSS-based lookups too. This means that if neither io.systemd.Multiplexer nor - io.systemd.NameSeviceSwitch are running look-ups into the the basic user/group + io.systemd.NameSeviceSwitch are running look-ups into the basic user/group databases will still work. diff --git a/man/vtable-example.c b/man/vtable-example.c index 98c20eec5..dede12bef 100644 --- a/man/vtable-example.c +++ b/man/vtable-example.c @@ -27,6 +27,30 @@ static const sd_bus_vtable vtable[] = { "s", SD_BUS_PARAM(returnstring), method, offsetof(object, number), SD_BUS_VTABLE_DEPRECATED), + SD_BUS_METHOD_WITH_ARGS_OFFSET( + "Method3", + SD_BUS_ARGS("s", string, "o", path), + SD_BUS_RESULT("s", returnstring), + method, offsetof(object, number), + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS( + "Method4", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + method, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_SIGNAL( + "Signal1", + "so", + 0), + SD_BUS_SIGNAL_WITH_NAMES( + "Signal2", + "so", SD_BUS_PARAM(string) SD_BUS_PARAM(path), + 0), + SD_BUS_SIGNAL_WITH_ARGS( + "Signal3", + SD_BUS_ARGS("s", string, "o", path), + 0), SD_BUS_WRITABLE_PROPERTY( "AutomaticStringProperty", "s", NULL, NULL, offsetof(object, name), diff --git a/man/yubikey-crypttab.sh b/man/yubikey-crypttab.sh index 9e65bae62..651246d6a 100644 --- a/man/yubikey-crypttab.sh +++ b/man/yubikey-crypttab.sh @@ -1,4 +1,4 @@ -# Make sure noone can read the files we generate but us +# Make sure no one can read the files we generate but us umask 077 # Destroy any old key on the Yubikey (careful!) @@ -23,8 +23,9 @@ dd if=/dev/urandom of=plaintext.bin bs=128 count=1 base64 < plaintext.bin | tr -d '\n\r\t ' > plaintext.base64 # Encrypt this newly generated (binary) LUKS decryption key using the public key whose private key is on the -# Yubikey, store the result in /etc/encrypted-luks-key.bin, where we'll look for it during boot. -sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/encrypted-luks-key.bin +# Yubikey, store the result in /etc/cryptsetup-keys.d/mytest.key, where we'll look for it during boot. +mkdir -p /etc/cryptsetup-keys.d +sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/cryptsetup-keys.d/mytest.key # Configure the LUKS decryption key on the LUKS device. We use very low pbkdf settings since the key already # has quite a high quality (it comes directly from /dev/urandom after all), and thus we don't need to do much @@ -40,8 +41,10 @@ shred -u plaintext.bin plaintext.base64 rm pubkey.pem # Test: Let's run systemd-cryptsetup to test if this all worked. The option string should contain the full -# PKCS#11 URI we have in the clipboard, it tells the tool how to decypher the encrypted LUKS key. -sudo systemd-cryptsetup attach mytest /dev/sdXn /etc/encrypted-luks-key.bin 'pkcs11-uri=pkcs11:…' +# PKCS#11 URI we have in the clipboard; it tells the tool how to decipher the encrypted LUKS key. Note that +# systemd-cryptsetup automatically searches for the encrypted key in /etc/cryptsetup-keys.d/, hence we do +# not need to specify the key file path explicitly here. +sudo systemd-cryptsetup attach mytest /dev/sdXn - 'pkcs11-uri=pkcs11:…' # If that worked, let's now add the same line persistently to /etc/crypttab, for the future. -sudo bash -c 'echo "mytest /dev/sdXn /etc/encrypted-luks-key \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab' +sudo bash -c 'echo "mytest /dev/sdXn - \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab' diff --git a/meson.build b/meson.build index c09115e06..dbbddb68e 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1+ project('systemd', 'c', - version : '245', + version : '246', license : 'LGPLv2+', default_options: [ 'c_std=gnu99', @@ -13,8 +13,8 @@ project('systemd', 'c', meson_version : '>= 0.46', ) -libsystemd_version = '0.28.0' -libudev_version = '1.6.17' +libsystemd_version = '0.29.0' +libudev_version = '1.6.18' # We need the same data in two different formats, ugh! # Also, for hysterical reasons, we use different variable @@ -32,27 +32,30 @@ substs.set('PROJECT_VERSION', meson.project_version(), # This is to be used instead of meson.source_root(), as the latter will return # the wrong result when systemd is being built as a meson subproject project_source_root = meson.current_source_dir() +project_build_root = meson.current_build_dir() relative_source_path = run_command('realpath', - '--relative-to=@0@'.format(meson.current_build_dir()), + '--relative-to=@0@'.format(project_build_root), project_source_root).stdout().strip() conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path) want_ossfuzz = get_option('oss-fuzz') want_libfuzzer = get_option('llvm-fuzz') -want_fuzzbuzz = get_option('fuzzbuzz') -if want_ossfuzz + want_libfuzzer + want_fuzzbuzz > 1 - error('only one of oss-fuzz, llvm-fuzz or fuzzbuzz can be specified') +if want_ossfuzz + want_libfuzzer > 1 + error('only one of oss-fuzz or llvm-fuzz can be specified') endif skip_deps = want_ossfuzz or want_libfuzzer -fuzzer_build = want_ossfuzz or want_libfuzzer or want_fuzzbuzz +fuzzer_build = want_ossfuzz or want_libfuzzer ##################################################################### # Try to install the git pre-commit hook -git_hook = run_command(join_paths(project_source_root, 'tools/add-git-hook.sh')) -if git_hook.returncode() == 0 - message(git_hook.stdout().strip()) +add_git_hook_sh = find_program('tools/add-git-hook.sh', required : false) +if add_git_hook_sh.found() + git_hook = run_command(add_git_hook_sh) + if git_hook.returncode() == 0 + message(git_hook.stdout().strip()) + endif endif ##################################################################### @@ -82,11 +85,17 @@ if rootprefixdir == '' endif rootprefixdir_noslash = rootprefixdir == '/' ? '' : rootprefixdir +have_standalone_binaries = get_option('standalone-binaries') + sysvinit_path = get_option('sysvinit-path') sysvrcnd_path = get_option('sysvrcnd-path') conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '', description : 'SysV init scripts and rcN.d links are supported') +if get_option('hibernate') and not get_option('initrd') + error('hibernate depends on initrd') +endif + conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max')) conf.set10('BUMP_PROC_SYS_FS_NR_OPEN', get_option('bump-proc-sys-fs-nr-open')) conf.set('HIGH_RLIMIT_NOFILE', 512*1024) @@ -196,7 +205,7 @@ memory_accounting_default = get_option('memory-accounting-default') status_unit_format_default = get_option('status-unit-format-default') conf.set_quoted('PKGSYSCONFDIR', pkgsysconfdir) -conf.set_quoted('SYSTEM_CONFIG_UNIT_PATH', join_paths(pkgsysconfdir, 'system')) +conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR', join_paths(pkgsysconfdir, 'system')) conf.set_quoted('SYSTEM_DATA_UNIT_PATH', systemunitdir) conf.set_quoted('SYSTEM_SYSVINIT_PATH', sysvinit_path) conf.set_quoted('SYSTEM_SYSVRCND_PATH', sysvrcnd_path) @@ -204,8 +213,8 @@ conf.set_quoted('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-loc conf.set('ANSI_OK_COLOR', 'ANSI_' + get_option('ok-color').underscorify().to_upper()) -conf.set_quoted('USER_CONFIG_UNIT_PATH', join_paths(pkgsysconfdir, 'user')) -conf.set_quoted('USER_DATA_UNIT_PATH', userunitdir) +conf.set_quoted('USER_CONFIG_UNIT_DIR', join_paths(pkgsysconfdir, 'user')) +conf.set_quoted('USER_DATA_UNIT_DIR', userunitdir) conf.set_quoted('CERTIFICATE_ROOT', get_option('certificate-root')) conf.set_quoted('CATALOG_DATABASE', join_paths(catalogstatedir, 'database')) conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent')) @@ -214,7 +223,6 @@ conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlib conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs')) conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs')) conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown')) -conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-sleep')) conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl')) conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent')) conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH', join_paths(bindir, 'systemd-stdio-bridge')) @@ -222,10 +230,10 @@ conf.set_quoted('ROOTPREFIX', rootprefixdir) conf.set_quoted('RANDOM_SEED_DIR', randomseeddir) conf.set_quoted('RANDOM_SEED', join_paths(randomseeddir, 'random-seed')) conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH', join_paths(rootlibexecdir, 'systemd-cryptsetup')) -conf.set_quoted('SYSTEM_GENERATOR_PATH', systemgeneratordir) -conf.set_quoted('USER_GENERATOR_PATH', usergeneratordir) -conf.set_quoted('SYSTEM_ENV_GENERATOR_PATH', systemenvgeneratordir) -conf.set_quoted('USER_ENV_GENERATOR_PATH', userenvgeneratordir) +conf.set_quoted('SYSTEM_GENERATOR_DIR', systemgeneratordir) +conf.set_quoted('USER_GENERATOR_DIR', usergeneratordir) +conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR', systemenvgeneratordir) +conf.set_quoted('USER_ENV_GENERATOR_DIR', userenvgeneratordir) conf.set_quoted('SYSTEM_SHUTDOWN_PATH', systemshutdowndir) conf.set_quoted('SYSTEM_SLEEP_PATH', systemsleepdir) conf.set_quoted('SYSTEMD_KBD_MODEL_MAP', join_paths(pkgdatadir, 'kbd-model-map')) @@ -291,17 +299,18 @@ substs.set('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-loc substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no') substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default) substs.set('HIGH_RLIMIT_NOFILE', conf.get('HIGH_RLIMIT_NOFILE')) -substs.set('BUILD_ROOT', meson.current_build_dir()) +substs.set('BUILD_ROOT', project_build_root) ##################################################################### cc = meson.get_compiler('c') pkgconfig = import('pkgconfig') -check_compilation_sh = find_program('tools/meson-check-compilation.sh') +check_compilation_sh = find_program('tools/check-compilation.sh') meson_build_sh = find_program('tools/meson-build.sh') want_tests = get_option('tests') slow_tests = want_tests != 'false' and get_option('slow-tests') +fuzz_tests = want_tests != 'false' and get_option('fuzz-tests') install_tests = get_option('install-tests') if add_languages('cpp', required : fuzzer_build) @@ -323,8 +332,6 @@ if want_libfuzzer endif elif want_ossfuzz fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine') -elif want_fuzzbuzz - fuzzing_engine = meson.get_compiler('cpp').find_library(get_option('fuzzbuzz-engine'), dirs: get_option('fuzzbuzz-engine-dir')) endif # Those generate many false positives, and we do not want to change the code to @@ -335,15 +342,6 @@ basic_disabled_warnings = [ '-Wno-unused-result', '-Wno-format-signedness', ] -if get_option('b_ndebug') == 'true' - # With asserts disabled with get a bunch of warnings about variables which - # are used only in the asserts. This is not useful at all, so let's just silence - # those warnings. - basic_disabled_warnings += [ - '-Wno-unused-variable', - '-Wno-unused-but-set-variable', - ] -endif possible_cc_flags = [ '-Werror=undef', @@ -378,9 +376,6 @@ possible_cc_flags = [ '-Wno-error=#warnings', # clang '-Wno-string-plus-int', # clang - # work-around for gcc 7.1 turning this on on its own. - '-Wno-error=nonnull', - # Disable -Wmaybe-uninitialized, since it's noisy on gcc 8 with # optimizations enabled, producing essentially false positives. '-Wno-maybe-uninitialized', @@ -654,7 +649,14 @@ endforeach ############################################################ -conf.set_quoted('FALLBACK_HOSTNAME', get_option('fallback-hostname')) +fallback_hostname = get_option('fallback-hostname') +if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0] == '-' + error('Invalid fallback-hostname configuration') + # A more extensive test is done in test-hostname-util. Let's catch + # the most obvious errors here so we don't fail with an assert later. +endif +conf.set_quoted('FALLBACK_HOSTNAME', fallback_hostname) + conf.set10('ENABLE_COMPAT_GATEWAY_HOSTNAME', get_option('compat-gateway-hostname')) gateway_hostnames = ['_gateway'] + (conf.get('ENABLE_COMPAT_GATEWAY_HOSTNAME') == 1 ? ['gateway'] : []) @@ -674,13 +676,17 @@ conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme) time_epoch = get_option('time-epoch') if time_epoch == -1 - source_date_epoch = run_command('sh', ['-c', 'echo "$SOURCE_DATE_EPOCH"']).stdout().strip() - if source_date_epoch != '' - time_epoch = source_date_epoch.to_int() - else - NEWS = files('NEWS') - time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout().to_int() + time_epoch = run_command('sh', ['-c', 'echo "$SOURCE_DATE_EPOCH"']).stdout().strip() + if time_epoch == '' and git.found() and run_command('test', '-e', '.git').returncode() == 0 + # If we're in a git repository, use the creation time of the latest git tag. + latest_tag = run_command('git', 'describe', '--abbrev=0', '--tags').stdout().strip() + time_epoch = run_command('git', 'log', '-1', '--format=%at', latest_tag).stdout() endif + if time_epoch == '' + NEWS = files('NEWS') + time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout() + endif + time_epoch = time_epoch.to_int() endif conf.set('TIME_EPOCH', time_epoch) @@ -929,6 +935,7 @@ conf.set10('HAVE_SELINUX', have) want_apparmor = get_option('apparmor') if want_apparmor != 'false' and not skip_deps libapparmor = dependency('libapparmor', + version : '>= 2.13', required : want_apparmor == 'true') have = libapparmor.found() else @@ -1032,6 +1039,8 @@ if want_libcryptsetup != 'false' and not skip_deps conf.set10('HAVE_CRYPT_SET_METADATA_SIZE', have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup)) + conf.set10('HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY', + have and cc.has_function('crypt_activate_by_signed_key', dependencies : libcryptsetup)) else have = false libcryptsetup = [] @@ -1139,8 +1148,8 @@ conf.set10('HAVE_OPENSSL', have) want_p11kit = get_option('p11kit') if want_p11kit != 'false' and not skip_deps libp11kit = dependency('p11-kit-1', - version : '>= 0.23.3', - required : want_p11kit == 'true') + version : '>= 0.23.3', + required : want_p11kit == 'true') have = libp11kit.found() else have = false @@ -1148,6 +1157,17 @@ else endif conf.set10('HAVE_P11KIT', have) +want_libfido2 = get_option('libfido2') +if want_libfido2 != 'false' and not skip_deps + libfido2 = dependency('libfido2', + required : want_libfido2 == 'true') + have = libfido2.found() +else + have = false + libfido2 = [] +endif +conf.set10('HAVE_LIBFIDO2', have) + want_elfutils = get_option('elfutils') if want_elfutils != 'false' and not skip_deps libdw = dependency('libdw', @@ -1185,24 +1205,38 @@ want_xz = get_option('xz') if want_xz != 'false' and not skip_deps libxz = dependency('liblzma', required : want_xz == 'true') - have = libxz.found() + have_xz = libxz.found() else - have = false + have_xz = false libxz = [] endif -conf.set10('HAVE_XZ', have) +conf.set10('HAVE_XZ', have_xz) want_lz4 = get_option('lz4') if want_lz4 != 'false' and not skip_deps liblz4 = dependency('liblz4', version : '>= 1.3.0', required : want_lz4 == 'true') - have = liblz4.found() + have_lz4 = liblz4.found() else - have = false + have_lz4 = false liblz4 = [] endif -conf.set10('HAVE_LZ4', have) +conf.set10('HAVE_LZ4', have_lz4) + +want_zstd = get_option('zstd') +if want_zstd != 'false' and not skip_deps + libzstd = dependency('libzstd', + required : want_zstd == 'true', + version : '>= 1.4.0') + have_zstd = libzstd.found() +else + have_zstd = false + libzstd = [] +endif +conf.set10('HAVE_ZSTD', have_zstd) + +conf.set10('HAVE_COMPRESSION', have_xz or have_lz4 or have_zstd) want_xkbcommon = get_option('xkbcommon') if want_xkbcommon != 'false' and not skip_deps @@ -1311,6 +1345,16 @@ conf.set('DEFAULT_DNS_OVER_TLS_MODE', 'DNS_OVER_TLS_' + default_dns_over_tls.underscorify().to_upper()) substs.set('DEFAULT_DNS_OVER_TLS_MODE', default_dns_over_tls) +default_mdns = get_option('default-mdns') +conf.set('DEFAULT_MDNS_MODE', + 'RESOLVE_SUPPORT_' + default_mdns.to_upper()) +substs.set('DEFAULT_MDNS_MODE', default_mdns) + +default_llmnr = get_option('default-llmnr') +conf.set('DEFAULT_LLMNR_MODE', + 'RESOLVE_SUPPORT_' + default_llmnr.to_upper()) +substs.set('DEFAULT_LLMNR_MODE', default_llmnr) + want_repart = get_option('repart') if want_repart != 'false' have = (conf.get('HAVE_OPENSSL') == 1 and @@ -1395,6 +1439,7 @@ foreach term : ['utmp', 'tmpfiles', 'hwdb', 'rfkill', + 'xdg-autostart', 'ldconfig', 'efi', 'tpm', @@ -1402,6 +1447,7 @@ foreach term : ['utmp', 'smack', 'gshadow', 'idn', + 'initrd', 'nss-myhostname', 'nss-systemd'] have = get_option(term) @@ -1483,6 +1529,7 @@ meson_apply_m4 = find_program('tools/meson-apply-m4.sh') includes = include_directories('src/basic', 'src/boot', + 'src/home', 'src/shared', 'src/systemd', 'src/journal', @@ -1496,6 +1543,7 @@ includes = include_directories('src/basic', 'src/libudev', 'src/core', 'src/shutdown', + 'src/xdg-autostart-generator', 'src/libsystemd/sd-bus', 'src/libsystemd/sd-device', 'src/libsystemd/sd-event', @@ -1542,6 +1590,7 @@ libsystemd = shared_library( dependencies : [threads, librt, libxz, + libzstd, liblz4], link_depends : libsystemd_sym, install : true, @@ -1565,6 +1614,7 @@ install_libsystemd_static = static_library( dependencies : [threads, librt, libxz, + libzstd, liblz4, libcap, libblkid, @@ -1573,7 +1623,7 @@ install_libsystemd_static = static_library( libgcrypt], c_args : libsystemd_c_args + (static_libsystemd_pic ? [] : ['-fno-PIC'])) -#Generate autosuspend rules +# Generate autosuspend rules make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py') ############################################################ @@ -1606,6 +1656,7 @@ subdir('src/nspawn') subdir('src/resolve') subdir('src/timedate') subdir('src/timesync') +subdir('src/tmpfiles') subdir('src/vconsole') subdir('src/boot/efi') @@ -1628,7 +1679,7 @@ test_dlopen = executable( build_by_default : want_tests != 'false') foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'], - ['systemd', 'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h'], + ['systemd', 'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h src/nss-systemd/nss-systemd.h'], ['mymachines', 'ENABLE_NSS_MYMACHINES'], ['resolve', 'ENABLE_NSS_RESOLVE']] @@ -1680,120 +1731,129 @@ endforeach ############################################################ -executable('systemd', - systemd_sources, - include_directories : includes, - link_with : [libcore, - libshared], - dependencies : [versiondep, - threads, - librt, - libseccomp, - libselinux, - libmount, - libblkid], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd', + systemd_sources, + include_directories : includes, + link_with : [libcore, + libshared], + dependencies : [versiondep, + threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) meson.add_install_script(meson_make_symlink, join_paths(rootlibexecdir, 'systemd'), join_paths(rootsbindir, 'init')) -exe = executable('systemd-analyze', - systemd_analyze_sources, - include_directories : includes, - link_with : [libcore, - libshared], - dependencies : [versiondep, - threads, - librt, - libseccomp, - libselinux, - libmount, - libblkid], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-analyze', + systemd_analyze_sources, + include_directories : includes, + link_with : [libcore, + libshared], + dependencies : [versiondep, + threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid], + install_rpath : rootlibexecdir, + install : get_option('analyze')) -executable('systemd-journald', - systemd_journald_sources, - include_directories : includes, - link_with : [libjournal_core, - libshared], - dependencies : [threads, - libxz, - liblz4, - libselinux], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-journald', + systemd_journald_sources, + include_directories : includes, + link_with : [libjournal_core, + libshared], + dependencies : [threads, + libxz, + liblz4, + libselinux, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -exe = executable('systemd-cat', - systemd_cat_sources, - include_directories : includes, - link_with : [libjournal_core, - libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-cat', + systemd_cat_sources, + include_directories : includes, + link_with : [libjournal_core, + libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true) -exe = executable('journalctl', - journalctl_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libqrencode, - libxz, - liblz4, - libpcre2], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'journalctl', + journalctl_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libqrencode, + libxz, + liblz4, + libpcre2, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -executable('systemd-getty-generator', - 'src/getty-generator/getty-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) +executable( + 'systemd-getty-generator', + 'src/getty-generator/getty-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) -executable('systemd-debug-generator', - 'src/debug-generator/debug-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) +executable( + 'systemd-debug-generator', + 'src/debug-generator/debug-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) -executable('systemd-run-generator', - 'src/run-generator/run-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) +executable( + 'systemd-run-generator', + 'src/run-generator/run-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) -executable('systemd-fstab-generator', - 'src/fstab-generator/fstab-generator.c', - include_directories : includes, - link_with : [libcore_shared, - libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) +executable( + 'systemd-fstab-generator', + 'src/fstab-generator/fstab-generator.c', + include_directories : includes, + link_with : [libcore_shared, + libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) if conf.get('ENABLE_ENVIRONMENT_D') == 1 - executable('30-systemd-environment-d-generator', - 'src/environment-d-generator/environment-d-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : userenvgeneratordir) + executable( + '30-systemd-environment-d-generator', + 'src/environment-d-generator/environment-d-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : userenvgeneratordir) meson.add_install_script(meson_make_symlink, join_paths(sysconfdir, 'environment'), @@ -1801,111 +1861,117 @@ if conf.get('ENABLE_ENVIRONMENT_D') == 1 endif if conf.get('ENABLE_HIBERNATE') == 1 - executable('systemd-hibernate-resume-generator', - 'src/hibernate-resume/hibernate-resume-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-hibernate-resume-generator', + 'src/hibernate-resume/hibernate-resume-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) - executable('systemd-hibernate-resume', - 'src/hibernate-resume/hibernate-resume.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-hibernate-resume', + 'src/hibernate-resume/hibernate-resume.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('HAVE_BLKID') == 1 - executable('systemd-gpt-auto-generator', - 'src/gpt-auto-generator/gpt-auto-generator.c', - 'src/shared/blkid-util.h', - include_directories : includes, - link_with : [libshared], - dependencies : libblkid, - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-gpt-auto-generator', + 'src/gpt-auto-generator/gpt-auto-generator.c', + 'src/shared/blkid-util.h', + include_directories : includes, + link_with : [libshared], + dependencies : libblkid, + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) - exe = executable('systemd-dissect', - 'src/dissect/dissect.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - public_programs += exe + public_programs += executable( + 'systemd-dissect', + 'src/dissect/dissect.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_RESOLVE') == 1 - executable('systemd-resolved', - systemd_resolved_sources, - include_directories : includes, - link_with : [libshared, - libbasic_gcrypt, - libsystemd_resolve_core], - dependencies : systemd_resolved_dependencies, - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-resolved', + systemd_resolved_sources, + include_directories : includes, + link_with : [libshared, + libbasic_gcrypt, + libsystemd_resolve_core], + dependencies : systemd_resolved_dependencies, + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('resolvectl', - resolvectl_sources, - include_directories : includes, - link_with : [libshared, - libbasic_gcrypt, - libsystemd_resolve_core], - dependencies : [threads, - libgpg_error, - libm, - libidn], - install_rpath : rootlibexecdir, - install : true) - public_programs += exe + public_programs += executable( + 'resolvectl', + resolvectl_sources, + include_directories : includes, + link_with : [libshared, + libbasic_gcrypt, + libsystemd_resolve_core], + dependencies : [threads, + libgpg_error, + libm, + libidn], + install_rpath : rootlibexecdir, + install : true) meson.add_install_script(meson_make_symlink, - join_paths(bindir, 'resolvectl'), - join_paths(rootsbindir, 'resolvconf')) + join_paths(bindir, 'resolvectl'), + join_paths(rootsbindir, 'resolvconf')) meson.add_install_script(meson_make_symlink, - join_paths(bindir, 'resolvectl'), - join_paths(bindir, 'systemd-resolve')) + join_paths(bindir, 'resolvectl'), + join_paths(bindir, 'systemd-resolve')) endif if conf.get('ENABLE_LOGIND') == 1 - executable('systemd-logind', - systemd_logind_sources, - include_directories : includes, - link_with : [liblogind_core, - libshared], - dependencies : [threads, - libacl], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-logind', + systemd_logind_sources, + include_directories : includes, + link_with : [liblogind_core, + libshared], + dependencies : [threads, + libacl], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('loginctl', - loginctl_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - liblz4, - libxz], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'loginctl', + loginctl_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + liblz4, + libxz, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) - exe = executable('systemd-inhibit', - 'src/login/inhibit.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'systemd-inhibit', + 'src/login/inhibit.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) if conf.get('HAVE_PAM') == 1 version_script_arg = join_paths(project_source_root, pam_systemd_sym) @@ -1933,70 +1999,75 @@ if conf.get('ENABLE_LOGIND') == 1 endif endif - executable('systemd-user-runtime-dir', - user_runtime_dir_sources, - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-user-runtime-dir', + user_runtime_dir_sources, + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('HAVE_PAM') == 1 - executable('systemd-user-sessions', - 'src/user-sessions/user-sessions.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-user-sessions', + 'src/user-sessions/user-sessions.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_BLKID') == 1 - exe = executable('bootctl', - 'src/boot/bootctl.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libblkid], - install_rpath : rootlibexecdir, - install : true) - public_programs += exe + public_programs += executable( + 'bootctl', + 'src/boot/bootctl.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libblkid], + install_rpath : rootlibexecdir, + install : true) - executable('systemd-bless-boot', - 'src/boot/bless-boot.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libblkid], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + public_programs += executable( + 'systemd-bless-boot', + 'src/boot/bless-boot.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libblkid], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-bless-boot-generator', - 'src/boot/bless-boot-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-bless-boot-generator', + 'src/boot/bless-boot-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) endif -executable('systemd-boot-check-no-failures', - 'src/boot/boot-check-no-failures.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libblkid], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - -exe = executable('systemd-socket-activate', 'src/activate/activate.c', - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +executable( + 'systemd-boot-check-no-failures', + 'src/boot/boot-check-no-failures.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libblkid], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) +public_programs += executable( + 'systemd-socket-activate', + 'src/activate/activate.c', + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true) if get_option('link-systemctl-shared') systemctl_link_with = [libshared] @@ -2007,111 +2078,122 @@ else libbasic_gcrypt] endif -exe = executable('systemctl', - 'src/systemctl/systemctl.c', - 'src/systemctl/sysv-compat.h', - 'src/systemctl/sysv-compat.c', - include_directories : includes, - link_with : systemctl_link_with, - dependencies : [threads, - libcap, - libselinux, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'systemctl', + 'src/systemctl/systemctl.c', + 'src/systemctl/sysv-compat.h', + 'src/systemctl/sysv-compat.c', + include_directories : includes, + link_with : systemctl_link_with, + dependencies : [threads, + libcap, + libselinux, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) if conf.get('ENABLE_PORTABLED') == 1 - executable('systemd-portabled', - systemd_portabled_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-portabled', + systemd_portabled_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('portablectl', 'src/portable/portablectl.c', - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'portablectl', + 'src/portable/portablectl.c', + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) endif if conf.get('ENABLE_USERDB') == 1 - executable('systemd-userwork', - systemd_userwork_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-userwork', + systemd_userwork_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-userdbd', - systemd_userdbd_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-userdbd', + systemd_userdbd_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('userdbctl', - userdbctl_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + public_programs += executable( + 'userdbctl', + userdbctl_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) endif if conf.get('ENABLE_HOMED') == 1 - executable('systemd-homework', - systemd_homework_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libcryptsetup, - libblkid, - libcrypt, - libopenssl, - libfdisk, - libp11kit], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-homework', + systemd_homework_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libcryptsetup, + libblkid, + libcrypt, + libopenssl, + libfdisk, + libp11kit, + libfido2], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-homed', - systemd_homed_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libcrypt, - libopenssl, - libpwquality], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-homed', + systemd_homed_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libcrypt, + libopenssl, + libpwquality], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('homectl', - homectl_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libcrypt, - libopenssl, - libp11kit, - libpwquality], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + public_programs += executable( + 'homectl', + homectl_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libcrypt, + libopenssl, + libp11kit, + libfido2, + libpwquality], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) if conf.get('HAVE_PAM') == 1 version_script_arg = join_paths(project_source_root, pam_systemd_home_sym) @@ -2134,122 +2216,160 @@ if conf.get('ENABLE_HOMED') == 1 endif endif -foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit'] +foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] + + (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : [])) meson.add_install_script(meson_make_symlink, join_paths(rootbindir, 'systemctl'), join_paths(rootsbindir, alias)) endforeach +meson.add_install_script(meson_make_symlink, + join_paths(rootbindir, 'udevadm'), + join_paths(rootlibexecdir, 'systemd-udevd')) + if conf.get('ENABLE_BACKLIGHT') == 1 - executable('systemd-backlight', - 'src/backlight/backlight.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-backlight', + 'src/backlight/backlight.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_RFKILL') == 1 - executable('systemd-rfkill', - 'src/rfkill/rfkill.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-rfkill', + 'src/rfkill/rfkill.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif -executable('systemd-system-update-generator', - 'src/system-update-generator/system-update-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) +executable( + 'systemd-system-update-generator', + 'src/system-update-generator/system-update-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) if conf.get('HAVE_LIBCRYPTSETUP') == 1 systemd_cryptsetup_sources = files(''' - src/cryptsetup/cryptsetup.c src/cryptsetup/cryptsetup-pkcs11.h + src/cryptsetup/cryptsetup-util.c + src/cryptsetup/cryptsetup-util.h + src/cryptsetup/cryptsetup.c '''.split()) if conf.get('HAVE_P11KIT') == 1 systemd_cryptsetup_sources += files('src/cryptsetup/cryptsetup-pkcs11.c') endif - executable('systemd-cryptsetup', - systemd_cryptsetup_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [libcryptsetup, - libp11kit], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-cryptsetup', + systemd_cryptsetup_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [libcryptsetup, + libp11kit], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-cryptsetup-generator', - 'src/cryptsetup/cryptsetup-generator.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libcryptsetup], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-cryptsetup-generator', + 'src/cryptsetup/cryptsetup-generator.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libcryptsetup], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) - executable('systemd-veritysetup', - 'src/veritysetup/veritysetup.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libcryptsetup], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-veritysetup', + 'src/veritysetup/veritysetup.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libcryptsetup], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-veritysetup-generator', - 'src/veritysetup/veritysetup-generator.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libcryptsetup], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-veritysetup-generator', + 'src/veritysetup/veritysetup-generator.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libcryptsetup], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) endif if conf.get('HAVE_SYSV_COMPAT') == 1 - executable('systemd-sysv-generator', - 'src/sysv-generator/sysv-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-sysv-generator', + 'src/sysv-generator/sysv-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) - executable('systemd-rc-local-generator', - 'src/rc-local-generator/rc-local-generator.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : systemgeneratordir) + executable( + 'systemd-rc-local-generator', + 'src/rc-local-generator/rc-local-generator.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : systemgeneratordir) +endif + +if conf.get('ENABLE_XDG_AUTOSTART') == 1 + executable( + 'systemd-xdg-autostart-generator', + 'src/xdg-autostart-generator/xdg-autostart-generator.c', + 'src/xdg-autostart-generator/xdg-autostart-service.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : usergeneratordir) + + executable( + 'systemd-xdg-autostart-condition', + 'src/xdg-autostart-generator/xdg-autostart-condition.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_HOSTNAMED') == 1 - executable('systemd-hostnamed', - 'src/hostname/hostnamed.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-hostnamed', + 'src/hostname/hostnamed.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('hostnamectl', - 'src/hostname/hostnamectl.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) - public_programs += exe + public_programs += executable( + 'hostnamectl', + 'src/hostname/hostnamectl.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) endif if conf.get('ENABLE_LOCALED') == 1 @@ -2260,43 +2380,45 @@ if conf.get('ENABLE_LOCALED') == 1 deps = [] endif - executable('systemd-localed', - systemd_localed_sources, - include_directories : includes, - link_with : [libshared], - dependencies : deps, - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-localed', + systemd_localed_sources, + include_directories : includes, + link_with : [libshared], + dependencies : deps, + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('localectl', - localectl_sources, - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) - public_programs += exe + public_programs += executable( + 'localectl', + localectl_sources, + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) endif if conf.get('ENABLE_TIMEDATED') == 1 - executable('systemd-timedated', - 'src/timedate/timedated.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-timedated', + 'src/timedate/timedated.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_TIMEDATECTL') == 1 - exe = executable('timedatectl', - 'src/timedate/timedatectl.c', - include_directories : includes, - install_rpath : rootlibexecdir, - link_with : [libshared], - dependencies : [libm], - install : true) - public_programs += exe + public_programs += executable( + 'timedatectl', + 'src/timedate/timedatectl.c', + include_directories : includes, + install_rpath : rootlibexecdir, + link_with : [libshared], + dependencies : [libm], + install : true) endif if conf.get('ENABLE_TIMESYNCD') == 1 @@ -2309,204 +2431,222 @@ if conf.get('ENABLE_TIMESYNCD') == 1 libbasic_gcrypt] endif - executable('systemd-timesyncd', - systemd_timesyncd_sources, - include_directories : includes, - link_with : [timesyncd_link_with], - dependencies : [threads, - libm], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-timesyncd', + systemd_timesyncd_sources, + include_directories : includes, + link_with : [timesyncd_link_with], + dependencies : [threads, + libm], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-time-wait-sync', - 'src/time-wait-sync/time-wait-sync.c', - include_directories : includes, - link_with : [timesyncd_link_with], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-time-wait-sync', + 'src/time-wait-sync/time-wait-sync.c', + include_directories : includes, + link_with : [timesyncd_link_with], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_MACHINED') == 1 - executable('systemd-machined', - systemd_machined_sources, - include_directories : includes, - link_with : [libmachine_core, - libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-machined', + systemd_machined_sources, + include_directories : includes, + link_with : [libmachine_core, + libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('machinectl', - 'src/machine/machinectl.c', - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'machinectl', + 'src/machine/machinectl.c', + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) endif if conf.get('ENABLE_IMPORTD') == 1 - executable('systemd-importd', - systemd_importd_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-importd', + systemd_importd_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - systemd_pull = executable('systemd-pull', - systemd_pull_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [versiondep, - libcurl, - libz, - libbzip2, - libxz, - libgcrypt], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + systemd_pull = executable( + 'systemd-pull', + systemd_pull_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [versiondep, + libcurl, + libz, + libbzip2, + libxz, + libgcrypt], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - systemd_import = executable('systemd-import', - systemd_import_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [libcurl, - libz, - libbzip2, - libxz], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + systemd_import = executable( + 'systemd-import', + systemd_import_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [libcurl, + libz, + libbzip2, + libxz], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - systemd_import_fs = executable('systemd-import-fs', - systemd_import_fs_sources, - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + systemd_import_fs = executable( + 'systemd-import-fs', + systemd_import_fs_sources, + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - systemd_export = executable('systemd-export', - systemd_export_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [libcurl, - libz, - libbzip2, - libxz], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + systemd_export = executable( + 'systemd-export', + systemd_export_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [libcurl, + libz, + libbzip2, + libxz], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) public_programs += [systemd_pull, systemd_import, systemd_import_fs, systemd_export] endif if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1 - exe = executable('systemd-journal-upload', - systemd_journal_upload_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [versiondep, - threads, - libcurl, - libgnutls, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - public_programs += exe + public_programs += executable( + 'systemd-journal-upload', + systemd_journal_upload_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [versiondep, + threads, + libcurl, + libgnutls, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1 - s_j_remote = executable('systemd-journal-remote', - systemd_journal_remote_sources, - include_directories : includes, - link_with : [libshared, - libsystemd_journal_remote], - dependencies : [threads, - libmicrohttpd, - libgnutls, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + public_programs += executable( + 'systemd-journal-remote', + systemd_journal_remote_sources, + include_directories : includes, + link_with : [libshared, + libsystemd_journal_remote], + dependencies : [threads, + libmicrohttpd, + libgnutls, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - s_j_gatewayd = executable('systemd-journal-gatewayd', - systemd_journal_gatewayd_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libmicrohttpd, - libgnutls, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - public_programs += [s_j_remote, s_j_gatewayd] + public_programs += executable( + 'systemd-journal-gatewayd', + systemd_journal_gatewayd_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libmicrohttpd, + libgnutls, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_COREDUMP') == 1 - executable('systemd-coredump', - systemd_coredump_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libacl, - libdw, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-coredump', + systemd_coredump_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libacl, + libdw, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('coredumpctl', - coredumpctl_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true) - public_programs += exe + public_programs += executable( + 'coredumpctl', + coredumpctl_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true) endif if conf.get('ENABLE_PSTORE') == 1 - executable('systemd-pstore', - systemd_pstore_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libacl, - libdw, - libxz, - liblz4], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-pstore', + systemd_pstore_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libacl, + libdw, + libxz, + liblz4, + libzstd], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_BINFMT') == 1 - exe = executable('systemd-binfmt', - 'src/binfmt/binfmt.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - public_programs += exe + public_programs += executable( + 'systemd-binfmt', + 'src/binfmt/binfmt.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) meson.add_install_script('sh', '-c', mkdir_p.format(binfmtdir)) @@ -2515,76 +2655,89 @@ if conf.get('ENABLE_BINFMT') == 1 endif if conf.get('ENABLE_REPART') == 1 - executable('systemd-repart', - systemd_repart_sources, - include_directories : includes, - link_with : [libshared], - dependencies : [threads, - libcryptsetup, - libblkid, - libfdisk, - libopenssl], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + exe = executable( + 'systemd-repart', + systemd_repart_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libcryptsetup, + libblkid, + libfdisk, + libopenssl], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) + + if want_tests != 'false' + test('test-repart', + test_repart_sh, + args : exe.full_path()) + endif endif if conf.get('ENABLE_VCONSOLE') == 1 - executable('systemd-vconsole-setup', - 'src/vconsole/vconsole-setup.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-vconsole-setup', + 'src/vconsole/vconsole-setup.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_RANDOMSEED') == 1 - executable('systemd-random-seed', - 'src/random-seed/random-seed.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-random-seed', + 'src/random-seed/random-seed.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif if conf.get('ENABLE_FIRSTBOOT') == 1 - executable('systemd-firstboot', - 'src/firstboot/firstboot.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libcrypt], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + executable( + 'systemd-firstboot', + 'src/firstboot/firstboot.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libcrypt], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) endif -executable('systemd-remount-fs', - 'src/remount-fs/remount-fs.c', - include_directories : includes, - link_with : [libcore_shared, - libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-remount-fs', + 'src/remount-fs/remount-fs.c', + include_directories : includes, + link_with : [libcore_shared, + libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -executable('systemd-machine-id-setup', - 'src/machine-id-setup/machine-id-setup-main.c', - include_directories : includes, - link_with : [libcore_shared, - libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) +executable( + 'systemd-machine-id-setup', + 'src/machine-id-setup/machine-id-setup-main.c', + include_directories : includes, + link_with : [libcore_shared, + libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -executable('systemd-fsck', - 'src/fsck/fsck.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-fsck', + 'src/fsck/fsck.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) executable('systemd-growfs', 'src/partition/growfs.c', @@ -2595,217 +2748,239 @@ executable('systemd-growfs', install : true, install_dir : rootlibexecdir) -executable('systemd-makefs', - 'src/partition/makefs.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-makefs', + 'src/partition/makefs.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -executable('systemd-sleep', - 'src/sleep/sleep.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-sleep', + 'src/sleep/sleep.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) install_data('src/sleep/sleep.conf', install_dir : pkgsysconfdir) -exe = executable('systemd-sysctl', - 'src/sysctl/sysctl.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) -public_programs += exe +public_programs += executable( + 'systemd-sysctl', + 'src/sysctl/sysctl.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -executable('systemd-ac-power', - 'src/ac-power/ac-power.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-ac-power', + 'src/ac-power/ac-power.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -exe = executable('systemd-detect-virt', - 'src/detect-virt/detect-virt.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-detect-virt', + 'src/detect-virt/detect-virt.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-delta', - 'src/delta/delta.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-delta', + 'src/delta/delta.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-escape', - 'src/escape/escape.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'systemd-escape', + 'src/escape/escape.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -exe = executable('systemd-notify', - 'src/notify/notify.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'systemd-notify', + 'src/notify/notify.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -executable('systemd-volatile-root', - 'src/volatile-root/volatile-root.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-volatile-root', + 'src/volatile-root/volatile-root.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : conf.get('ENABLE_INITRD') == 1, + install_dir : rootlibexecdir) -executable('systemd-cgroups-agent', - 'src/cgroups-agent/cgroups-agent.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-cgroups-agent', + 'src/cgroups-agent/cgroups-agent.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -exe = executable('systemd-id128', - 'src/id128/id128.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-id128', + 'src/id128/id128.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-path', - 'src/path/path.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-path', + 'src/path/path.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-ask-password', - 'src/ask-password/ask-password.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'systemd-ask-password', + 'src/ask-password/ask-password.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -executable('systemd-reply-password', - 'src/reply-password/reply-password.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-reply-password', + 'src/reply-password/reply-password.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -exe = executable('systemd-tty-ask-password-agent', - 'src/tty-ask-password-agent/tty-ask-password-agent.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) -public_programs += exe +public_programs += executable( + 'systemd-tty-ask-password-agent', + 'src/tty-ask-password-agent/tty-ask-password-agent.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) -exe = executable('systemd-cgls', - 'src/cgls/cgls.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-cgls', + 'src/cgls/cgls.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-cgtop', - 'src/cgtop/cgtop.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-cgtop', + 'src/cgtop/cgtop.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -executable('systemd-initctl', - 'src/initctl/initctl.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-initctl', + 'src/initctl/initctl.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : (conf.get('HAVE_SYSV_COMPAT') == 1), + install_dir : rootlibexecdir) -exe = executable('systemd-mount', - 'src/mount/mount-tool.c', - include_directories : includes, - link_with : [libshared], - dependencies: [libmount], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-mount', + 'src/mount/mount-tool.c', + include_directories : includes, + link_with : [libshared], + dependencies: [libmount], + install_rpath : rootlibexecdir, + install : true) meson.add_install_script(meson_make_symlink, 'systemd-mount', join_paths(bindir, 'systemd-umount')) -exe = executable('systemd-run', - 'src/run/run.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-run', + 'src/run/run.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) -exe = executable('systemd-stdio-bridge', - 'src/stdio-bridge/stdio-bridge.c', - include_directories : includes, - link_with : [libshared], - dependencies : [versiondep], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-stdio-bridge', + 'src/stdio-bridge/stdio-bridge.c', + include_directories : includes, + link_with : [libshared], + dependencies : [versiondep], + install_rpath : rootlibexecdir, + install : true) -exe = executable('busctl', - 'src/busctl/busctl.c', - 'src/busctl/busctl-introspect.c', - 'src/busctl/busctl-introspect.h', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'busctl', + 'src/busctl/busctl.c', + 'src/busctl/busctl-introspect.c', + 'src/busctl/busctl-introspect.h', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true) if conf.get('ENABLE_SYSUSERS') == 1 - exe = executable('systemd-sysusers', - 'src/sysusers/sysusers.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'systemd-sysusers', + 'src/sysusers/sysusers.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) + + if have_standalone_binaries + public_programs += executable( + 'systemd-sysusers.standalone', + 'src/sysusers/sysusers.c', + include_directories : includes, + link_with : [libshared_static, + libbasic, + libbasic_gcrypt, + libsystemd_static, + libjournal_client], + install : true, + install_dir : rootbindir) + endif endif if conf.get('ENABLE_TMPFILES') == 1 - exe = executable('systemd-tmpfiles', - 'src/tmpfiles/tmpfiles.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libacl], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + exe = executable( + 'systemd-tmpfiles', + systemd_tmpfiles_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [libacl], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) public_programs += exe if want_tests != 'false' @@ -2814,112 +2989,114 @@ if conf.get('ENABLE_TMPFILES') == 1 # https://github.com/mesonbuild/meson/issues/2681 args : exe.full_path()) endif + + if have_standalone_binaries + public_programs += executable( + 'systemd-tmpfiles.standalone', + systemd_tmpfiles_sources, + include_directories : includes, + link_with : [libshared_static, + libbasic, + libbasic_gcrypt, + libsystemd_static, + libjournal_client], + dependencies : [libacl], + install : true, + install_dir : rootbindir) + endif endif if conf.get('ENABLE_HWDB') == 1 - exe = executable('systemd-hwdb', - 'src/hwdb/hwdb.c', - 'src/libsystemd/sd-hwdb/hwdb-internal.h', - include_directories : includes, - link_with : [libudev_static], - install_rpath : udev_rpath, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'systemd-hwdb', + 'src/hwdb/hwdb.c', + 'src/libsystemd/sd-hwdb/hwdb-internal.h', + include_directories : includes, + link_with : [libudev_static], + install_rpath : udev_rpath, + install : true, + install_dir : rootbindir) endif if conf.get('ENABLE_QUOTACHECK') == 1 - executable('systemd-quotacheck', - 'src/quotacheck/quotacheck.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-quotacheck', + 'src/quotacheck/quotacheck.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) endif -exe = executable('systemd-socket-proxyd', - 'src/socket-proxy/socket-proxyd.c', - include_directories : includes, - link_with : [libshared], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) -public_programs += exe +public_programs += executable( + 'systemd-socket-proxyd', + 'src/socket-proxy/socket-proxyd.c', + include_directories : includes, + link_with : [libshared], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -exe = executable('systemd-udevd', - systemd_udevd_sources, - include_directories : includes, - c_args : '-DLOG_REALM=LOG_REALM_UDEV', - link_with : [libudev_core, - libsystemd_network, - libudev_static], - dependencies : [versiondep, - threads, - libkmod, - libidn, - libacl, - libblkid], - install_rpath : udev_rpath, - install : true, - install_dir : rootlibexecdir) -public_programs += exe +public_programs += executable( + 'udevadm', + udevadm_sources, + c_args : '-DLOG_REALM=LOG_REALM_UDEV', + include_directories : includes, + link_with : [libudev_core, + libsystemd_network, + libudev_static], + dependencies : [versiondep, + threads, + libkmod, + libidn, + libacl, + libblkid], + install_rpath : udev_rpath, + install : true, + install_dir : rootbindir) -exe = executable('udevadm', - udevadm_sources, - c_args : '-DLOG_REALM=LOG_REALM_UDEV', - include_directories : includes, - link_with : [libudev_core, - libsystemd_network, - libudev_static], - dependencies : [versiondep, - threads, - libkmod, - libidn, - libacl, - libblkid], - install_rpath : udev_rpath, - install : true, - install_dir : rootbindir) -public_programs += exe +executable( + 'systemd-shutdown', + systemd_shutdown_sources, + include_directories : includes, + link_with : [libcore_shared, + libshared], + dependencies : [libmount], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -executable('systemd-shutdown', - systemd_shutdown_sources, - include_directories : includes, - link_with : [libcore_shared, - libshared], - dependencies : [libmount], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-update-done', + 'src/update-done/update-done.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) -executable('systemd-update-done', - 'src/update-done/update-done.c', - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) - -executable('systemd-update-utmp', - 'src/update-utmp/update-utmp.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libaudit], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-update-utmp', + 'src/update-utmp/update-utmp.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libaudit], + install_rpath : rootlibexecdir, + install : (conf.get('ENABLE_UTMP') == 1), + install_dir : rootlibexecdir) if conf.get('HAVE_KMOD') == 1 - executable('systemd-modules-load', - 'src/modules-load/modules-load.c', - include_directories : includes, - link_with : [libshared], - dependencies : [libkmod], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-modules-load', + 'src/modules-load/modules-load.c', + include_directories : includes, + link_with : [libshared], + dependencies : [libkmod], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) meson.add_install_script('sh', '-c', mkdir_p.format(modulesloaddir)) @@ -2927,66 +3104,77 @@ if conf.get('HAVE_KMOD') == 1 mkdir_p.format(join_paths(sysconfdir, 'modules-load.d'))) endif -exe = executable('systemd-nspawn', - systemd_nspawn_sources, - include_directories : includes, - link_with : [libcore_shared, - libnspawn_core, - libshared], - dependencies : [libblkid, - libseccomp], - install_rpath : rootlibexecdir, - install : true) -public_programs += exe +public_programs += executable( + 'systemd-nspawn', + systemd_nspawn_sources, + include_directories : includes, + link_with : [libcore_shared, + libnspawn_core, + libshared], + dependencies : [libblkid, + libseccomp], + install_rpath : rootlibexecdir, + install : true) if conf.get('ENABLE_NETWORKD') == 1 - executable('systemd-networkd', - systemd_networkd_sources, - include_directories : network_include_dir, - link_with : [libnetworkd_core, - libsystemd_network, - libudev_static, - networkd_link_with], - dependencies : [threads], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-networkd', + systemd_networkd_sources, + include_directories : network_include_dir, + link_with : [libnetworkd_core, + libsystemd_network, + libudev_static, + networkd_link_with], + dependencies : [threads], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - executable('systemd-networkd-wait-online', - systemd_networkd_wait_online_sources, - include_directories : includes, - link_with : [libnetworkd_core, - networkd_link_with], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + executable( + 'systemd-networkd-wait-online', + systemd_networkd_wait_online_sources, + include_directories : includes, + link_with : [libnetworkd_core, + networkd_link_with], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) - exe = executable('networkctl', - networkctl_sources, - include_directories : includes, - link_with : [libsystemd_network, - networkd_link_with], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) - public_programs += exe + public_programs += executable( + 'networkctl', + networkctl_sources, + include_directories : includes, + link_with : [libsystemd_network, + networkd_link_with], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootbindir) - executable('systemd-network-generator', - network_generator_sources, - include_directories : includes, - link_with : [networkd_link_with], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) + exe = executable( + 'systemd-network-generator', + network_generator_sources, + include_directories : includes, + link_with : [networkd_link_with], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) + + 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()) + endif endif -executable('systemd-sulogin-shell', - ['src/sulogin-shell/sulogin-shell.c'], - include_directories : includes, - link_with : [libshared], - install_rpath : rootlibexecdir, - install : true, - install_dir : rootlibexecdir) +executable( + 'systemd-sulogin-shell', + ['src/sulogin-shell/sulogin-shell.c'], + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) ############################################################ @@ -2995,7 +3183,7 @@ custom_target( output : 'systemd-runtest.env', command : ['sh', '-c', '{ ' + 'echo SYSTEMD_TEST_DATA=@0@; '.format(join_paths(project_source_root, 'test')) + - 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(meson.current_build_dir(), 'catalog')) + + 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(project_build_root, 'catalog')) + '} >@OUTPUT@'], build_by_default : true) @@ -3100,45 +3288,42 @@ endif fuzzer_exes = [] if get_option('tests') != 'false' -foreach tuple : fuzzers - sources = tuple[0] - link_with = tuple[1].length() > 0 ? tuple[1] : [libshared] - dependencies = tuple[2] - defs = tuple.length() >= 4 ? tuple[3] : [] - incs = tuple.length() >= 5 ? tuple[4] : includes - link_args = [] + foreach tuple : fuzzers + sources = tuple[0] + link_with = tuple[1].length() > 0 ? tuple[1] : [libshared] + dependencies = tuple[2] + defs = tuple.length() >= 4 ? tuple[3] : [] + incs = tuple.length() >= 5 ? tuple[4] : includes + link_args = [] - if want_ossfuzz or want_fuzzbuzz - dependencies += fuzzing_engine - elif want_libfuzzer - if fuzzing_engine.found() + if want_ossfuzz dependencies += fuzzing_engine + elif want_libfuzzer + if fuzzing_engine.found() + dependencies += fuzzing_engine + else + link_args += ['-fsanitize=fuzzer'] + endif else - link_args += ['-fsanitize=fuzzer'] + sources += 'src/fuzz/fuzz-main.c' endif - else - sources += 'src/fuzz/fuzz-main.c' - endif - if want_fuzzbuzz - sources += 'src/fuzz/fuzzer-entry-point.c' - endif + name = sources[0].split('/')[-1].split('.')[0] - name = sources[0].split('/')[-1].split('.')[0] - - fuzzer_exes += executable( - name, - sources, - include_directories : [incs, include_directories('src/fuzz')], - link_with : link_with, - dependencies : dependencies, - c_args : defs, - link_args: link_args, - install : false) -endforeach + fuzzer_exes += executable( + name, + sources, + include_directories : [incs, include_directories('src/fuzz')], + link_with : link_with, + dependencies : dependencies, + c_args : defs, + link_args: link_args, + install : false) + endforeach endif -run_target('fuzzers', +run_target( + 'fuzzers', depends : fuzzer_exes, command : ['true']) @@ -3147,8 +3332,8 @@ run_target('fuzzers', make_directive_index_py = find_program('tools/make-directive-index.py') make_man_index_py = find_program('tools/make-man-index.py') xml_helper_py = find_program('tools/xml_helper.py') -hwdb_update_sh = find_program('tools/meson-hwdb-update.sh') -autosuspend_update_sh = find_program('tools/meson-autosuspend-update.sh') +hwdb_update_sh = find_program('tools/hwdb-update.sh') +autosuspend_update_sh = find_program('tools/autosuspend-update.sh') subdir('sysctl.d') subdir('sysusers.d') @@ -3189,13 +3374,13 @@ meson.add_install_script('sh', '-c', 'touch $DESTDIR@0@'.format(prefixdir)) ############################################################ -meson_check_help = find_program('tools/meson-check-help.sh') +check_help = find_program('tools/check-help.sh') foreach exec : public_programs name = exec.full_path().split('/')[-1] if want_tests != 'false' test('check-help-' + name, - meson_check_help, + check_help, args : exec.full_path()) endif endforeach @@ -3228,7 +3413,7 @@ foreach tuple : sanitizers if name != prev if want_tests == 'false' message('Not compiling @0@ because tests is set to false'.format(name)) - elif slow_tests + elif slow_tests or fuzz_tests exe = custom_target( name, output : name, @@ -3238,14 +3423,16 @@ foreach tuple : sanitizers '@OUTPUT@'], build_by_default : true) else - message('Not compiling @0@ because slow-tests is set to false'.format(name)) + message('Not compiling @0@ because slow-tests/fuzz-tests is set to false'.format(name)) endif endif prev = name - if want_tests != 'false' and slow_tests + if want_tests != 'false' and (slow_tests or fuzz_tests) test('@0@:@1@:@2@'.format(b, c, sanitizer), env, + env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'], + timeout : 60, args : [exe.full_path(), join_paths(project_source_root, p)]) endif @@ -3274,10 +3461,10 @@ if git.found() endif if git.found() - meson_git_contrib_sh = find_program('tools/meson-git-contrib.sh') + git_contrib_sh = find_program('tools/git-contrib.sh') run_target( 'git-contrib', - command : [meson_git_contrib_sh]) + command : [git_contrib_sh]) endif if git.found() @@ -3301,11 +3488,11 @@ endif ############################################################ -meson_check_api_docs_sh = find_program('tools/meson-check-api-docs.sh') +check_api_docs_sh = find_program('tools/check-api-docs.sh') run_target( 'check-api-docs', depends : [man, libsystemd, libudev], - command : [meson_check_api_docs_sh, libsystemd.full_path(), libudev.full_path()]) + command : [check_api_docs_sh, libsystemd.full_path(), libudev.full_path()]) ############################################################ watchdog_opt = service_watchdog == '' ? 'disabled' : service_watchdog @@ -3354,6 +3541,8 @@ status = [ 'default DNSSEC mode: @0@'.format(default_dnssec), 'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls), + 'default mDNS mode: @0@'.format(default_mdns), + 'default LLMNR mode: @0@'.format(default_llmnr), 'default cgroup hierarchy: @0@'.format(default_hierarchy), 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme), 'default KillUserProcesses setting: @0@'.format(kill_user_processes), @@ -3403,6 +3592,7 @@ foreach tuple : [ ['pwquality'], ['libfdisk'], ['p11kit'], + ['libfido2'], ['AUDIT'], ['IMA'], ['AppArmor'], @@ -3411,6 +3601,7 @@ foreach tuple : [ ['SMACK'], ['zlib'], ['xz'], + ['zstd'], ['lz4'], ['bzip2'], ['ACL'], @@ -3421,6 +3612,7 @@ foreach tuple : [ ['openssl'], ['libcurl'], ['idn'], + ['initrd'], ['libidn2'], ['libidn'], ['libiptc'], @@ -3436,6 +3628,7 @@ foreach tuple : [ ['randomseed'], ['backlight'], ['rfkill'], + ['xdg-autostart'], ['logind'], ['machined'], ['portabled'], @@ -3483,10 +3676,13 @@ foreach tuple : [ ['debug siphash'], ['valgrind', conf.get('VALGRIND') == 1], ['trace logging', conf.get('LOG_TRACE') == 1], + ['install tests', install_tests], ['link-udev-shared', get_option('link-udev-shared')], ['link-systemctl-shared', get_option('link-systemctl-shared')], ['link-networkd-shared', get_option('link-networkd-shared')], ['link-timesyncd-shared', get_option('link-timesyncd-shared')], + ['kernel-install', get_option('kernel-install')], + ['systemd-analyze', get_option('analyze')], ] if tuple.length() >= 2 diff --git a/meson_options.txt b/meson_options.txt index 212724d7d..fd73d5e68 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,6 +26,8 @@ option('static-libsystemd', type : 'combo', option('static-libudev', type : 'combo', choices : ['false', 'true', 'pic', 'no-pic'], description : '''install a static library for libudev''') +option('standalone-binaries', type : 'boolean', value : 'false', + description : '''also build standalone versions of supported binaries''') option('sysvinit-path', type : 'string', value : '/etc/init.d', description : 'the directory where the SysV init scripts are located') @@ -35,6 +37,8 @@ option('telinit-path', type : 'string', value : '/lib/sysvinit/telinit', description : 'path to telinit') option('rc-local', type : 'string', value : '/etc/rc.local') +option('initrd', type: 'boolean', + description : 'install services for use when running systemd in initrd') option('quotaon-path', type : 'string', description : 'path to quotaon') option('quotacheck-path', type : 'string', description : 'path to quotacheck') @@ -140,6 +144,8 @@ option('hwdb', type : 'boolean', description : 'support for the hardware database') option('rfkill', type : 'boolean', description : 'support for the rfkill tools') +option('xdg-autostart', type : 'boolean', + description : 'install the xdg-autostart-generator and unit') option('man', type : 'combo', choices : ['auto', 'true', 'false'], value : 'false', description : 'build and install man pages') @@ -230,6 +236,14 @@ option('default-dns-over-tls', type : 'combo', description : 'default DNS-over-TLS mode', choices : ['yes', 'opportunistic', 'no'], value : 'no') +option('default-mdns', type : 'combo', + choices : ['yes', 'resolve', 'no'], + description : 'default MulticastDNS mode', + value : 'yes') +option('default-llmnr', type : 'combo', + choices : ['yes', 'resolve', 'no'], + description : 'default LLMNR mode', + value : 'yes') option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'], description : 'DNS-over-TLS support') option('dns-servers', type : 'string', @@ -281,7 +295,7 @@ option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'], option('libcurl', type : 'combo', choices : ['auto', 'true', 'false'], description : 'libcurl support') option('idn', type : 'boolean', - description : 'use IDN when printing host names') + description : 'use IDN when printing hostnames') option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'], description : 'libidn2 support') option('libidn', type : 'combo', choices : ['auto', 'true', 'false'], @@ -298,6 +312,8 @@ option('openssl', type : 'combo', choices : ['auto', 'true', 'false'], description : 'openssl support') option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'], description : 'p11kit support') +option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'FIDO2 support') option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'], description : 'elfutils support') option('zlib', type : 'combo', choices : ['auto', 'true', 'false'], @@ -308,6 +324,8 @@ option('xz', type : 'combo', choices : ['auto', 'true', 'false'], description : 'xz compression support') option('lz4', type : 'combo', choices : ['auto', 'true', 'false'], description : 'lz4 compression support') +option('zstd', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'zstd compression support') option('xkbcommon', type : 'combo', choices : ['auto', 'true', 'false'], description : 'xkbcommon keymap support') option('pcre2', type : 'combo', choices : ['auto', 'true', 'false'], @@ -339,6 +357,8 @@ option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'], description : 'enable extra tests with =unsafe') option('slow-tests', type : 'boolean', value : 'false', description : 'run the slow tests by default') +option('fuzz-tests', type : 'boolean', value : 'false', + description : 'run the fuzzer regression tests by default') option('install-tests', type : 'boolean', value : 'false', description : 'install test executables') @@ -354,9 +374,7 @@ option('oss-fuzz', type : 'boolean', value : 'false', description : 'build against oss-fuzz') option('llvm-fuzz', type : 'boolean', value : 'false', description : 'build against LLVM libFuzzer') -option('fuzzbuzz', type : 'boolean', value : 'false', - description : 'build against FuzzBuzz') -option('fuzzbuzz-engine', type : 'string', - description : 'the name of the FuzzBuzz fuzzing engine') -option('fuzzbuzz-engine-dir', type : 'string', - description : 'the directory where the FuzzBuzz fuzzing engine is') +option('kernel-install', type: 'boolean', value: 'true', + description : 'install kernel-install and associated files') +option('analyze', type: 'boolean', value: 'true', + description : 'install systemd-analyze') diff --git a/mkosi.build b/mkosi.build index 16ac2e9d0..4a13f1075 100755 --- a/mkosi.build +++ b/mkosi.build @@ -42,6 +42,14 @@ fi if [ ! -f "$BUILDDIR"/build.ninja ] ; then sysvinit_path=`realpath /etc/init.d` + init_path=`realpath /sbin/init 2>/dev/null` + if [ -z "$init_path" ] ; then + rootprefix="" + else + rootprefix=${init_path%/lib/systemd/systemd} + rootprefix=/${rootprefix#/} + fi + nobody_user=`id -u -n 65534 2> /dev/null` if [ "$nobody_user" != "" ] ; then # Validate that we can translate forth and back @@ -76,11 +84,17 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then fi fi - meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group" + meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D "rootprefix=$rootprefix" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group" fi ninja -C "$BUILDDIR" all -[ "$WITH_TESTS" = 0 ] || ninja -C "$BUILDDIR" test +if [ "$WITH_TESTS" = 1 ] ; then + for id in 1 2 3; do + groupadd -g $id testgroup$id || : + done + + ninja -C "$BUILDDIR" test +fi ninja -C "$BUILDDIR" install mkdir -p "$DESTDIR"/etc diff --git a/mkosi.default b/mkosi.default deleted file mode 120000 index 2718c9e2a..000000000 --- a/mkosi.default +++ /dev/null @@ -1 +0,0 @@ -.mkosi/mkosi.fedora \ No newline at end of file diff --git a/network/80-vm-vt.network b/network/80-vm-vt.network new file mode 100644 index 000000000..4144034cd --- /dev/null +++ b/network/80-vm-vt.network @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This network file matches vt-* TUN/TAP devices and applies a similar +# configuration as ve-* to provide NAT/DHCP to VMs. + +[Match] +Name=vt-* +Driver=tun + +[Network] +# Default to using a /28 prefix, giving up to 13 addresses per VM. +Address=0.0.0.0/28 +LinkLocalAddressing=yes +DHCPServer=yes +IPMasquerade=yes +LLDP=yes +EmitLLDP=customer-bridge diff --git a/network/meson.build b/network/meson.build index 544dcf438..99a650eac 100644 --- a/network/meson.build +++ b/network/meson.build @@ -4,6 +4,7 @@ if conf.get('ENABLE_NETWORKD') == 1 install_data('80-container-host0.network', '80-container-ve.network', '80-container-vz.network', + '80-vm-vt.network', '80-wifi-adhoc.network', '80-wifi-ap.network.example', '80-wifi-station.network.example', diff --git a/po/be.po b/po/be.po index 55d3c8228..65e5064fe 100644 --- a/po/be.po +++ b/po/be.po @@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Неабходна аўтэнтыфікацыя для перачытання стану systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Усталяваць імя вузла" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Неабходна аўтэнтыфікацыя для ўсталявання імя вузла." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Усталяваць статычнае імя вузла" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Неабходна аўтэнтыфікацыя для ўсталявання як статычнага так і прыгожага імя " "вузла." diff --git a/po/be@latin.po b/po/be@latin.po index bbd9223b7..1ce3efa8f 100644 --- a/po/be@latin.po +++ b/po/be@latin.po @@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Nieabchodna aŭtentyfikacyja dlia pieračytannia stanu systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Ustaliavać imia vuzla" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Nieabchodna aŭtentyfikacyja dlia ŭstaliavannia imia vuzla." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Ustaliavać statyčnaje imia vuzla" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Nieabchodna aŭtentyfikacyja dlia ŭstaliavannia jak statyčnaha tak i " "pryhožaha imia vuzla." diff --git a/po/bg.po b/po/bg.po index 38b952782..0e1f50736 100644 --- a/po/bg.po +++ b/po/bg.po @@ -69,21 +69,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "За презареждане на състоянието на systemd е необходима идентификация." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Задаване на име на машината" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "За задаване на име на локалната машина е необходима идентификация." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Задаване на статично име на машината" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "За задаване на статично име на локалната машина е необходима идентификация." diff --git a/po/ca.po b/po/ca.po index dd8cc56e3..68123ca5d 100644 --- a/po/ca.po +++ b/po/ca.po @@ -73,21 +73,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Es requereix autenticació per tornar a carregar l'estat de systemd." #: src/hostname/org.freedesktop.hostname1.policy:22 -msgid "Set host name" +msgid "Set hostname" msgstr "Estableix el nom d'amfitrió" #: src/hostname/org.freedesktop.hostname1.policy:23 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Es requereix autenticació per establir el nom d'amfitrió local." #: src/hostname/org.freedesktop.hostname1.policy:32 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Estableix el nom d'amfitrió estàtic" #: src/hostname/org.freedesktop.hostname1.policy:33 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Es requereix autenticació per establir el nom d'amfitrió local configurat " "estàticament, així com el nom bonic d'amfitrió." diff --git a/po/cs.po b/po/cs.po index 819800132..8805dce0d 100644 --- a/po/cs.po +++ b/po/cs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2020-02-29 15:12+0000\n" -"PO-Revision-Date: 2020-03-01 13:58+0100\n" +"POT-Creation-Date: 2020-05-30 13:27+0000\n" +"PO-Revision-Date: 2020-07-01 16:40+0200\n" "Last-Translator: Daniel Rusek \n" "Language-Team: Czech\n" "Language: cs\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 2.3\n" +"X-Generator: Poedit 2.3.1\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -120,21 +120,21 @@ msgid "" msgstr "Pro změnu hesla domovského adresáře uživatele je vyžadováno ověření." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Nastavit název stroje" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Pro nastavení lokálního názvu stroje je vyžadováno ověření." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Nastavit statický název stroje" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Pro nastavení staticky konfigurovaného názvu lokálního stroje, stejně tak " "pro změnu uživatelsky přívětivého jména je vyžadováno ověření." @@ -740,26 +740,34 @@ msgid "Authentication is required to reset DNS settings." msgstr "Pro resetování nastavení DNS je vyžadováno ověření." #: src/network/org.freedesktop.network1.policy:143 +msgid "DHCP server sends force renew message" +msgstr "DHCP server posílá zprávu vynuceného obnovení" + +#: src/network/org.freedesktop.network1.policy:144 +msgid "Authentication is required to send force renew message." +msgstr "Pro poslání zprávy vynuceného obnovení je vyžadováno ověření." + +#: src/network/org.freedesktop.network1.policy:154 msgid "Renew dynamic addresses" msgstr "Obnovit dynamické adresy" -#: src/network/org.freedesktop.network1.policy:144 +#: src/network/org.freedesktop.network1.policy:155 msgid "Authentication is required to renew dynamic addresses." msgstr "Pro obnovení dynamických adres je vyžadováno ověření." -#: src/network/org.freedesktop.network1.policy:154 +#: src/network/org.freedesktop.network1.policy:165 msgid "Reload network settings" msgstr "Znovu načíst nastavení sítě" -#: src/network/org.freedesktop.network1.policy:155 +#: src/network/org.freedesktop.network1.policy:166 msgid "Authentication is required to reload network settings." msgstr "Pro opětovné načtení nastavení sítě je vyžadováno ověření." -#: src/network/org.freedesktop.network1.policy:165 +#: src/network/org.freedesktop.network1.policy:176 msgid "Reconfigure network interface" msgstr "Přenastavit síťové rozhraní" -#: src/network/org.freedesktop.network1.policy:166 +#: src/network/org.freedesktop.network1.policy:177 msgid "Authentication is required to reconfigure network interface." msgstr "Pro přenastavení síťového rozhraní je vyžadováno ověření." @@ -853,40 +861,47 @@ msgid "" "shall be enabled." msgstr "Pro kontrolu synchronizace času ze sítě je vyžadováno ověření." -#: src/core/dbus-unit.c:356 +#: src/core/dbus-unit.c:358 msgid "Authentication is required to start '$(unit)'." msgstr "Pro spuštění „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:357 +#: src/core/dbus-unit.c:359 msgid "Authentication is required to stop '$(unit)'." msgstr "Pro vypnutí „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:358 +#: src/core/dbus-unit.c:360 msgid "Authentication is required to reload '$(unit)'." msgstr "Pro opětovné načtení „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360 +#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362 msgid "Authentication is required to restart '$(unit)'." msgstr "Pro restart „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:532 +#: src/core/dbus-unit.c:534 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." msgstr "Pro odeslání UNIX signálu procesům „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:563 +#: src/core/dbus-unit.c:565 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "Pro resetování chybného stavu „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:596 +#: src/core/dbus-unit.c:598 msgid "Authentication is required to set properties on '$(unit)'." msgstr "Pro nastavení vlastností na „$(unit)” je vyžadováno ověření." -#: src/core/dbus-unit.c:705 +#: src/core/dbus-unit.c:707 msgid "" "Authentication is required to delete files and directories associated with " "'$(unit)'." msgstr "" "Pro odstranění souborů nebo adresářů souvisejících s „$(unit)” je vyžadováno " "ověření." + +#: src/core/dbus-unit.c:756 +msgid "" +"Authentication is required to freeze or thaw the processes of '$(unit)' unit." +msgstr "" +"Pro zmrazení nebo rozmrazení procesů jednotky „$(unit)” je vyžadováno " +"ověření." diff --git a/po/da.po b/po/da.po index 7dee61286..276c9e10c 100644 --- a/po/da.po +++ b/po/da.po @@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Autentificering er nødvendig for at genindlæse systemd tilstanden." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Sæt værtsnavn" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Autentificering er nødvendig for at sætte værtsnavn." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Sæt statisk værstnavn" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Autentificering er nødvendig for at sætte det statisk konfigurerede lokale " "værtsnavn, lige så vel som det pæne værtsnavn." diff --git a/po/de.po b/po/de.po index cca79207d..c5c753bb0 100644 --- a/po/de.po +++ b/po/de.po @@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Legitimierung ist zum erneuten Laden des systemd-Zustands notwendig." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Rechnername festlegen" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Legitimierung ist zum Festlegen des lokalen Rechnernamens notwendig" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Statischen Rechnernamen festlegen" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Authentifizierung ist erforderlich, um den statisch geänderten, lokalen " "Rechnernamen, sowie den beschönigten Rechnernamen festzulegen." diff --git a/po/el.po b/po/el.po index d96bf1626..c4225334d 100644 --- a/po/el.po +++ b/po/el.po @@ -76,21 +76,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Ορισμός ονόματος οικοδεσπότη" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Ορισμός στατικού ονόματος οικοδεσπότη" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Απαιτείται πιστοποίηση για να ορίσετε το στατικά ρυθμισμένο όνομα τοπικού " "οικοδεσπότη, καθώς και το pretty όνομα οικοδεσπότη." diff --git a/po/es.po b/po/es.po index aa586a729..adfe827bc 100644 --- a/po/es.po +++ b/po/es.po @@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Se requiere autenticación para recargar el estado de systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Establecer el nombre del equipo" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Se requiere autenticación para establecer el nombre del equipo local." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Establecer nombre estático del equipo" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Se requiere autenticación para establecer el nombre estático de equipo " "local, así como el nombre visible del equipo." diff --git a/po/fr.po b/po/fr.po index 0c5e05301..0999cf9dc 100644 --- a/po/fr.po +++ b/po/fr.po @@ -125,21 +125,21 @@ msgstr "" "d'un utilisateur." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Définir le nom d'hôte" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Authentification requise pour définir le nom d'hôte local." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Définir le nom d'hôte statique" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Authentification requise pour définir le nom d'hôte local de manière " "statique, tout comme le nom d'hôte familier." diff --git a/po/gl.po b/po/gl.po index 7796154aa..147d34da8 100644 --- a/po/gl.po +++ b/po/gl.po @@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Requírese autenticación para recargar o estado de systemd." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Estabelecer o nome do equipo" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Requírese autenticación para estabelecer o nome local do equiupo." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Estabelecer o nome do equipo estático" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Requírese autenticación para estabelecer de forma o nome do equipo local " "estabelecido de forma estática, así como o nome do equipo lexíbel por " diff --git a/po/hr.po b/po/hr.po index d77fee7df..8385d5ae9 100644 --- a/po/hr.po +++ b/po/hr.po @@ -118,21 +118,21 @@ msgid "" msgstr "Potrebna je ovjera za promjenu lozinke osobnog prostora korisnika." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Postavi naziv računala" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Potrebna je ovjera za postavljanje naziva lokalnog računala." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Postavi nepromjenjivi naziv račumala" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, " "kao i prijatnog naziva računala." diff --git a/po/hu.po b/po/hu.po index c9c8bc786..e4e5d540b 100644 --- a/po/hu.po +++ b/po/hu.po @@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Gépnév beállítása" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Hitelesítés szükséges a helyi gépnév beállításához." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Statikus gépnév beállítása" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Hitelesítés szükséges a statikusan megadott helyi gépnév, valamint a szép " "gépnév beállításához." diff --git a/po/id.po b/po/id.po index 66dd11ec7..efc2ad700 100644 --- a/po/id.po +++ b/po/id.po @@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Otentikasi diperlukan untuk memuat ulang keadaan systemd." #: src/hostname/org.freedesktop.hostname1.policy:22 -msgid "Set host name" +msgid "Set hostname" msgstr "Setel nama host" #: src/hostname/org.freedesktop.hostname1.policy:23 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Otentikasi diperlukan untuk menata nama host lokal." #: src/hostname/org.freedesktop.hostname1.policy:32 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Setel nama host statik" #: src/hostname/org.freedesktop.hostname1.policy:33 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Otentikasi diperlukan untuk menata nama host lokal yang dikonfigurasi " "statik, maupun nama host cantik." diff --git a/po/it.po b/po/it.po index 141058699..a2f45bd1d 100644 --- a/po/it.po +++ b/po/it.po @@ -124,21 +124,21 @@ msgstr "" "dell'utente." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Configura il nome host" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Autenticazione richiesta per configurare il nome host locale." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Configura il nome host statico" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Autenticazione richiesta per configurare staticamente il nome host locale e " "il nome host descrittivo." diff --git a/po/ja.po b/po/ja.po index 0847af1f7..7662bec2e 100644 --- a/po/ja.po +++ b/po/ja.po @@ -110,21 +110,21 @@ msgid "Authentication is required to change the password of a user's home area." msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。" #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "ホスト名の設定" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "ホスト名を設定するには認証が必要です。" #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "静的なホスト名の設定" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "静的なホスト名を設定するには認証が必要です。" #: src/hostname/org.freedesktop.hostname1.policy:41 diff --git a/po/ko.po b/po/ko.po index 53899fd9e..301a32240 100644 --- a/po/ko.po +++ b/po/ko.po @@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "systemd 상태를 다시 불러오려면 인증이 필요합니다." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "호스트 이름 설정" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "로컬 호스트 이름을 설정하려면 인증이 필요합니다." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "정적 호스트 이름 설정" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "로컬 호스트 이름을 모양새를 갖춘 호스트 이름 처럼 정적으로 설정하려면 인증" "이 필요합니다." diff --git a/po/lt.po b/po/lt.po index 3350b56ee..2f03fd65e 100644 --- a/po/lt.po +++ b/po/lt.po @@ -68,22 +68,22 @@ msgid "Authentication is required to reload the systemd state." msgstr "Norint iš naujo įkelti systemd būseną, reikia patvirtinti tapatybę." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Nustatyti serverio pavadinimą" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "" "Norint nustatyti vietinio serverio pavadinimą, reikia nustatyti tapatybę." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Nustatyti statinį serverio pavadinimą" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Norint nustatyti statiškai sukonfigūruotą serverio pavadinimą, o taip pat " "lengvai įsimenamą serverio pavadinimą, reikia nustatyti tapatybę." diff --git a/po/pl.po b/po/pl.po index 4e8657421..e94225598 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2020-02-29 15:12+0000\n" -"PO-Revision-Date: 2020-03-01 14:45+0100\n" +"POT-Creation-Date: 2020-05-01 15:36+0000\n" +"PO-Revision-Date: 2020-05-03 13:50+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" @@ -130,21 +130,21 @@ msgstr "" "użytkownika." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Ustawienie nazwy komputera" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Wymagane jest uwierzytelnienie, aby ustawić nazwę lokalnego komputera." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Ustawienie statycznej nazwy komputera" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić statycznie skonfigurowaną nazwę " "lokalnego komputera, a także jego nazwę czytelną dla człowieka." @@ -776,26 +776,35 @@ msgid "Authentication is required to reset DNS settings." msgstr "Wymagane jest uwierzytelnienie, aby przywrócić ustawienia DNS." #: src/network/org.freedesktop.network1.policy:143 +msgid "DHCP server sends force renew message" +msgstr "Serwer DHCP wysyła komunikat wymuszonego odnowienia" + +#: src/network/org.freedesktop.network1.policy:144 +msgid "Authentication is required to send force renew message." +msgstr "" +"Wymagane jest uwierzytelnienie, aby wysłać komunikat wymuszonego odnowienia." + +#: src/network/org.freedesktop.network1.policy:154 msgid "Renew dynamic addresses" msgstr "Odnowienie adresów dynamicznych" -#: src/network/org.freedesktop.network1.policy:144 +#: src/network/org.freedesktop.network1.policy:155 msgid "Authentication is required to renew dynamic addresses." msgstr "Wymagane jest uwierzytelnienie, aby odnowić adresy dynamiczne." -#: src/network/org.freedesktop.network1.policy:154 +#: src/network/org.freedesktop.network1.policy:165 msgid "Reload network settings" msgstr "Ponowne wczytanie ustawień sieci" -#: src/network/org.freedesktop.network1.policy:155 +#: src/network/org.freedesktop.network1.policy:166 msgid "Authentication is required to reload network settings." msgstr "Wymagane jest uwierzytelnienie, aby ponownie wczytać ustawienia sieci." -#: src/network/org.freedesktop.network1.policy:165 +#: src/network/org.freedesktop.network1.policy:176 msgid "Reconfigure network interface" msgstr "Ponowna konfiguracja interfejsu sieciowego" -#: src/network/org.freedesktop.network1.policy:166 +#: src/network/org.freedesktop.network1.policy:177 msgid "Authentication is required to reconfigure network interface." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie skonfigurować interfejs " @@ -896,25 +905,25 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację " "czasu przez sieć." -#: src/core/dbus-unit.c:356 +#: src/core/dbus-unit.c:358 msgid "Authentication is required to start '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”." -#: src/core/dbus-unit.c:357 +#: src/core/dbus-unit.c:359 msgid "Authentication is required to stop '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”." -#: src/core/dbus-unit.c:358 +#: src/core/dbus-unit.c:360 msgid "Authentication is required to reload '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”." -#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360 +#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362 msgid "Authentication is required to restart '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”." -#: src/core/dbus-unit.c:532 +#: src/core/dbus-unit.c:534 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." @@ -922,18 +931,18 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów " "jednostki „$(unit)”." -#: src/core/dbus-unit.c:563 +#: src/core/dbus-unit.c:565 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) " "jednostki „$(unit)”." -#: src/core/dbus-unit.c:596 +#: src/core/dbus-unit.c:598 msgid "Authentication is required to set properties on '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”." -#: src/core/dbus-unit.c:705 +#: src/core/dbus-unit.c:707 msgid "" "Authentication is required to delete files and directories associated with " "'$(unit)'." @@ -941,6 +950,13 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby usunąć pliki i katalogi powiązane " "z jednostką „$(unit)”." +#: src/core/dbus-unit.c:756 +msgid "" +"Authentication is required to freeze or thaw the processes of '$(unit)' unit." +msgstr "" +"Wymagane jest uwierzytelnienie, aby zamrozić lub odmrozić procesy jednostki " +"„$(unit)”." + #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress" #~ msgstr "" #~ "Naciśnięcie klawiszy Ctrl+C anuluje wszystkie trwające procesy " diff --git a/po/pt_BR.po b/po/pt_BR.po index 06706fe8f..ef61e47ca 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -3,14 +3,14 @@ # Brazilian Portuguese translation for systemd. # Enrico Nicoletto , 2014. # Filipe Brandenburger , 2018. -# Rafael Fontenelle , 2015-2019. +# Rafael Fontenelle , 2015-2020. # msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2019-09-08 15:28+0000\n" -"PO-Revision-Date: 2019-09-08 19:00-0300\n" +"POT-Creation-Date: 2020-05-29 03:32+0000\n" +"PO-Revision-Date: 2020-05-30 09:10-0300\n" "Last-Translator: Rafael Fontenelle \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" @@ -18,7 +18,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" -"X-Generator: Gtranslator 3.32.0\n" +"X-Generator: Gtranslator 3.36.0\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" @@ -73,22 +73,76 @@ msgstr "Recarregar o estado do sistema" msgid "Authentication is required to reload the systemd state." msgstr "É necessária autenticação para recarregar o estado do sistema." +#: src/home/org.freedesktop.home1.policy:13 +msgid "Create a home area" +msgstr "Criar uma área home" + +#: src/home/org.freedesktop.home1.policy:14 +msgid "Authentication is required to create a user's home area." +msgstr "É necessária autenticação para criar a área home de um usuário." + +#: src/home/org.freedesktop.home1.policy:23 +msgid "Remove a home area" +msgstr "Remover uma área home" + +#: src/home/org.freedesktop.home1.policy:24 +msgid "Authentication is required to remove a user's home area." +msgstr "É necessária autenticação para remover a área home de um usuário." + +#: src/home/org.freedesktop.home1.policy:33 +msgid "Check credentials of a home area" +msgstr "Verificar credenciais de uma área home" + +#: src/home/org.freedesktop.home1.policy:34 +msgid "" +"Authentication is required to check credentials against a user's home area." +msgstr "" +"É necessária autenticação para verificar credenciais da área home de um " +"usuário." + +#: src/home/org.freedesktop.home1.policy:43 +msgid "Update a home area" +msgstr "Atualizar uma área home" + +#: src/home/org.freedesktop.home1.policy:44 +msgid "Authentication is required to update a user's home area." +msgstr "É necessária autenticação para atualizar a área home de um usuário." + +#: src/home/org.freedesktop.home1.policy:53 +msgid "Resize a home area" +msgstr "Redimensionar uma área home" + +#: src/home/org.freedesktop.home1.policy:54 +msgid "Authentication is required to resize a user's home area." +msgstr "" +"É necessária autenticação para redimensionar a área home de um usuário." + +#: src/home/org.freedesktop.home1.policy:63 +msgid "Change password of a home area" +msgstr "Alterar senha de uma área home" + +#: src/home/org.freedesktop.home1.policy:64 +msgid "" +"Authentication is required to change the password of a user's home area." +msgstr "" +"É necessária autenticação para alterar a senha da área home de um usuário." + #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Definir nome de máquina" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." -msgstr "É necessária autenticação para definir nome de máquina local." +msgid "Authentication is required to set the local hostname." +msgstr "É necessária autenticação para definir o nome de máquina local." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Definir nome estático de máquina" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "É necessária autenticação para definir o nome de máquina local configurado " "estaticamente, assim como o nome apresentável de máquina." @@ -293,8 +347,7 @@ msgid "Flush device to seat attachments" msgstr "Liberar dispositivo para conexões da estação" #: src/login/org.freedesktop.login1.policy:149 -msgid "" -"Authentication is required to reset how devices are attached to seats." +msgid "Authentication is required to reset how devices are attached to seats." msgstr "" "É necessária autenticação para redefinir a quantidade de dispositivos " "conectados na estação." @@ -325,8 +378,8 @@ msgstr "Desligar o sistema enquanto um aplicativo solicitou inibição" #: src/login/org.freedesktop.login1.policy:181 msgid "" -"Authentication is required to power off the system while an application " -"is inhibiting this." +"Authentication is required to power off the system while an application is " +"inhibiting this." msgstr "" "É necessária autenticação para desligar o sistema enquanto um aplicativo " "solicitou inibição." @@ -345,8 +398,8 @@ msgstr "Reiniciar o sistema enquanto outros usuários estiverem conectados" #: src/login/org.freedesktop.login1.policy:203 msgid "" -"Authentication is required to reboot the system while other users are " -"logged in." +"Authentication is required to reboot the system while other users are logged " +"in." msgstr "" "É necessária autenticação para reiniciar o sistema enquanto outros usuários " "estiverem conectados." @@ -357,8 +410,8 @@ msgstr "Reiniciar o sistema enquanto um aplicativo solicitou inibição" #: src/login/org.freedesktop.login1.policy:214 msgid "" -"Authentication is required to reboot the system while an application " -"is inhibiting this." +"Authentication is required to reboot the system while an application is " +"inhibiting this." msgstr "" "É necessária autenticação para reiniciar o sistema enquanto um aplicativo " "solicitou inibição." @@ -377,8 +430,8 @@ msgstr "Parar o sistema enquanto outros usuários estão logados" #: src/login/org.freedesktop.login1.policy:236 msgid "" -"Authentication is required to halt the system while other users are " -"logged in." +"Authentication is required to halt the system while other users are logged " +"in." msgstr "" "É necessária autenticação para parar o sistema enquanto outros usuários " "estejam logados." @@ -389,8 +442,8 @@ msgstr "Parar o sistema enquanto um aplicativo solicitou inibição" #: src/login/org.freedesktop.login1.policy:247 msgid "" -"Authentication is required to halt the system while an application asked " -"to inhibit it." +"Authentication is required to halt the system while an application is " +"inhibiting this." msgstr "" "É necessária autenticação para parar o sistema enquanto um aplicativo " "solicitou inibição." @@ -421,8 +474,8 @@ msgstr "Suspender o sistema enquanto um aplicativo solicitou inibição" #: src/login/org.freedesktop.login1.policy:279 msgid "" -"Authentication is required to suspend the system while an application " -"is inhibiting this." +"Authentication is required to suspend the system while an application is " +"inhibiting this." msgstr "" "É necessária autenticação para suspender o sistema enquanto um aplicativo " "solicitou inibição." @@ -453,8 +506,8 @@ msgstr "Hibernar o sistema enquanto um aplicativo solicitou inibição" #: src/login/org.freedesktop.login1.policy:311 msgid "" -"Authentication is required to hibernate the system while an application " -"is inhibiting this." +"Authentication is required to hibernate the system while an application is " +"inhibiting this." msgstr "" "É necessária autenticação para hibernar o sistema enquanto um aplicativo " "solicitou inibição." @@ -464,8 +517,7 @@ msgid "Manage active sessions, users and seats" msgstr "Gerenciar estações, usuários e sessões ativas" #: src/login/org.freedesktop.login1.policy:322 -msgid "" -"Authentication is required to manage active sessions, users and seats." +msgid "Authentication is required to manage active sessions, users and seats." msgstr "" "É necessária autenticação para gerenciar estações, usuários e sessões ativas." @@ -509,7 +561,7 @@ msgid "" "boot loader menu." msgstr "" "É necessária autenticação para indicar para o carregador de inicialização " -"iniciar seu menu" +"iniciar seu menu." #: src/login/org.freedesktop.login1.policy:374 msgid "Indicate to the boot loader to boot a specific entry" @@ -522,7 +574,7 @@ msgid "" "specific boot loader entry." msgstr "" "É necessária autenticação para indicar para o carregador de inicializar " -"iniciar uma entrada específica" +"iniciar uma entrada específica." #: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" @@ -532,6 +584,14 @@ msgstr "Definir uma mensagem de parede" msgid "Authentication is required to set a wall message" msgstr "É necessária autenticação para definir uma mensagem de parede" +#: src/login/org.freedesktop.login1.policy:395 +msgid "Change Session" +msgstr "Alterar sessão" + +#: src/login/org.freedesktop.login1.policy:396 +msgid "Authentication is required to change the virtual terminal." +msgstr "É necessária autenticação para alterar o terminal virtual." + #: src/machine/org.freedesktop.machine1.policy:22 msgid "Log into a local container" msgstr "Conectar a um contêiner local" @@ -701,16 +761,48 @@ msgid "Revert NTP settings" msgstr "Reverter configurações de NTP" #: src/network/org.freedesktop.network1.policy:122 -msgid "Authentication is required to revert NTP settings." -msgstr "É necessária autenticação para reverter as configurações de NTP." +msgid "Authentication is required to reset NTP settings." +msgstr "É necessária autenticação para redefinir as configurações de NTP." #: src/network/org.freedesktop.network1.policy:132 msgid "Revert DNS settings" msgstr "Reverter configurações de DNS" #: src/network/org.freedesktop.network1.policy:133 -msgid "Authentication is required to revert DNS settings." -msgstr "É necessária autenticação para reverter as configurações de DNS." +msgid "Authentication is required to reset DNS settings." +msgstr "É necessária autenticação para redefinir as configurações de DNS." + +#: src/network/org.freedesktop.network1.policy:143 +msgid "DHCP server sends force renew message" +msgstr "Servidor DHCP envia mensagem de renovação forçada" + +#: src/network/org.freedesktop.network1.policy:144 +msgid "Authentication is required to send force renew message." +msgstr "É necessária autenticação para enviar mensagem de renovação forçada." + +#: src/network/org.freedesktop.network1.policy:154 +msgid "Renew dynamic addresses" +msgstr "Renovar endereços dinâmicos" + +#: src/network/org.freedesktop.network1.policy:155 +msgid "Authentication is required to renew dynamic addresses." +msgstr "É necessária autenticação para renovar endereços dinâmicos." + +#: src/network/org.freedesktop.network1.policy:165 +msgid "Reload network settings" +msgstr "Recarregar configurações de rede" + +#: src/network/org.freedesktop.network1.policy:166 +msgid "Authentication is required to reload network settings." +msgstr "É necessária autenticação para recarregar as configurações de rede." + +#: src/network/org.freedesktop.network1.policy:176 +msgid "Reconfigure network interface" +msgstr "Reconfigurar interface de rede" + +#: src/network/org.freedesktop.network1.policy:177 +msgid "Authentication is required to reconfigure network interface." +msgstr "É necessária autenticação para reconfigurar a interface de rede." #: src/portable/org.freedesktop.portable1.policy:13 msgid "Inspect a portable service image" @@ -764,9 +856,9 @@ msgid "Revert name resolution settings" msgstr "Reverter configurações de resolução de nome" #: src/resolve/org.freedesktop.resolve1.policy:133 -msgid "Authentication is required to revert name resolution settings." +msgid "Authentication is required to reset name resolution settings." msgstr "" -"É necessária autenticação para reverter as configurações de resolução de " +"É necessária autenticação para redefinir as configurações de resolução de " "nome." #: src/timedate/org.freedesktop.timedate1.policy:22 @@ -787,7 +879,7 @@ msgstr "É necessária autenticação para definir o fuso horário do sistema." #: src/timedate/org.freedesktop.timedate1.policy:43 msgid "Set RTC to local timezone or UTC" -msgstr "Definir o relógio do sistema (RTC) para fuso horário local ou UTC" +msgstr "Definir RTC para fuso horário local ou UTC" #: src/timedate/org.freedesktop.timedate1.policy:44 msgid "" @@ -809,23 +901,23 @@ msgstr "" "É necessária autenticação para controlar se deve ser habilitada, ou não, a " "sincronização de horário através de rede." -#: src/core/dbus-unit.c:354 +#: src/core/dbus-unit.c:358 msgid "Authentication is required to start '$(unit)'." msgstr "É necessária autenticação para iniciar “$(unit)”." -#: src/core/dbus-unit.c:355 +#: src/core/dbus-unit.c:359 msgid "Authentication is required to stop '$(unit)'." msgstr "É necessária autenticação para parar “$(unit)”." -#: src/core/dbus-unit.c:356 +#: src/core/dbus-unit.c:360 msgid "Authentication is required to reload '$(unit)'." msgstr "É necessária autenticação para recarregar “$(unit)”." -#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358 +#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362 msgid "Authentication is required to restart '$(unit)'." msgstr "É necessária autenticação para reiniciar “$(unit)”." -#: src/core/dbus-unit.c:530 +#: src/core/dbus-unit.c:534 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." @@ -833,16 +925,16 @@ msgstr "" "É necessária autenticação para enviar um sinal UNIX para os processos de " "“$(unit)”." -#: src/core/dbus-unit.c:561 +#: src/core/dbus-unit.c:565 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" "É necessária autenticação para reiniciar o estado “failed” de “$(unit)”." -#: src/core/dbus-unit.c:594 +#: src/core/dbus-unit.c:598 msgid "Authentication is required to set properties on '$(unit)'." msgstr "É necessária autenticação para definir propriedades em “$(unit)”." -#: src/core/dbus-unit.c:703 +#: src/core/dbus-unit.c:707 msgid "" "Authentication is required to delete files and directories associated with " "'$(unit)'." @@ -850,5 +942,9 @@ msgstr "" "É necessária autenticação para excluir arquivos e diretórios associados com " "“$(unit)”." -#~ msgid "Authentication is required to kill '$(unit)'." -#~ msgstr "É necessária autenticação para matar “$(unit)”." +#: src/core/dbus-unit.c:756 +msgid "" +"Authentication is required to freeze or thaw the processes of '$(unit)' unit." +msgstr "" +"É necessária autenticação para congelar ou descongelar os processos da " +"unidade “$(unit)”." diff --git a/po/ro.po b/po/ro.po index e22f1a110..869cf6b62 100644 --- a/po/ro.po +++ b/po/ro.po @@ -73,21 +73,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Autentificarea este necesară pentru a reîncărca starea systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Stabilește numele de server" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Autentificarea este necesară pentru a stabili numele de server local." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Stabilește numele de server static" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Autentificarea este necesara pentru a stabili numele de server static " "configurat local, precum și numele lung de server." diff --git a/po/ru.po b/po/ru.po index 35da66eef..f13c51f30 100644 --- a/po/ru.po +++ b/po/ru.po @@ -134,21 +134,21 @@ msgstr "" " пройти аутентификацию." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Настроить имя компьютера" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Настроить статическое имя компьютера" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Чтобы настроить статическое имя компьютера, а также его «красивое» имя, " "необходимо пройти аутентификацию." diff --git a/po/sk.po b/po/sk.po index ced96e804..971df90b2 100644 --- a/po/sk.po +++ b/po/sk.po @@ -72,21 +72,21 @@ msgstr "" "Vyžaduje sa overenie totožnosti na znovu načítanie stavu systému systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Nastavenie názvu hostiteľa" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Vyžaduje sa overenie totožnosti na nastavenie názvu hostiteľa." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Nastavenie nemenného názvu hostiteľa" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Vyžaduje sa overenie totožnosti na nastavenie pevne určeného názvu miestneho " "hostiteľa, známeho ako zrozumiteľný názov hostiteľa." diff --git a/po/sr.po b/po/sr.po index 3d0bebd50..a62f5cffe 100644 --- a/po/sr.po +++ b/po/sr.po @@ -72,21 +72,21 @@ msgstr "" "Потребно је да се идентификујете да бисте поново учитали стање систем-деа." #: src/hostname/org.freedesktop.hostname1.policy.in:22 -msgid "Set host name" +msgid "Set hostname" msgstr "Постави назив машине" #: src/hostname/org.freedesktop.hostname1.policy.in:23 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Потребно је да се идентификујете да бисте поставили назив машине." #: src/hostname/org.freedesktop.hostname1.policy.in:32 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Постави статички назив машине" #: src/hostname/org.freedesktop.hostname1.policy.in:33 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Потребно је да се идентификујете да бисте поставили статички назив машине и " "да бисте поставили леп назив машине." diff --git a/po/sv.po b/po/sv.po index 5f40d455a..c8bd2ae27 100644 --- a/po/sv.po +++ b/po/sv.po @@ -68,21 +68,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "Autentisering krävs för att läsa om tillståndet för systemd." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "Ange värdnamn" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Autentisering krävs för att ställa in lokalt värdnamn." #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Ange statiskt värdnamn" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Autentisering krävs för att ställa in det statiskt konfigurerade lokala " "värdnamnet såväl som det stiliga värdnamnet." diff --git a/po/tr.po b/po/tr.po index 86c13448e..b2af9e900 100644 --- a/po/tr.po +++ b/po/tr.po @@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "systemd durumunu yeniden yüklemek kimlik doğrulaması gerektiriyor." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Makine adını ayarla" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Yerel makine adını ayarlamak kimlik doğrulaması gerektiriyor." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Statik makine adı ayarla" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Statik olarak yapılandırılmış konak makine adını ve yerel makine adını " "ayarlamak kimlik doğrulaması gerektiriyor." diff --git a/po/uk.po b/po/uk.po index c2902797d..f0a702802 100644 --- a/po/uk.po +++ b/po/uk.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2020-01-30 15:31+0000\n" -"PO-Revision-Date: 2020-02-07 12:37+0200\n" +"POT-Creation-Date: 2020-03-22 04:04+0000\n" +"PO-Revision-Date: 2020-03-25 18:40+0200\n" "Last-Translator: Yuri Chornoivan \n" "Language-Team: Ukrainian \n" "Language: uk\n" @@ -94,8 +94,8 @@ msgstr "Перевірка реєстраційних даних для дост 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" @@ -119,27 +119,28 @@ 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." +msgid "" +"Authentication is required to change the password of a user's home area." msgstr "" -"Для зміни пароля для доступу до домашньої теки користувача слід пройти" -" розпізнавання." +"Для зміни пароля для доступу до домашньої теки користувача слід пройти " +"розпізнавання." #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "Встановити назву вузла" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "Потрібна автентифікація, щоб встановити назву локального вузла." #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "Встановити статичну назву вузла" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "" "Потрібна автентифікація, щоб вказати статично налаштовану назву локального " "вузла, так само й форматовану." @@ -345,8 +346,7 @@ msgid "Flush device to seat attachments" msgstr "Очисний пристрій для під'єднань до місця" #: src/login/org.freedesktop.login1.policy:149 -msgid "" -"Authentication is required to reset how devices are attached to seats." +msgid "Authentication is required to reset how devices are attached to seats." msgstr "" "Потрібна автентифікація, щоб перезапустити спосіб під'єднання до місць." @@ -375,8 +375,8 @@ msgstr "Вимкнути систему, коли програми намага #: src/login/org.freedesktop.login1.policy:181 msgid "" -"Authentication is required to power off the system while an application " -"is inhibiting this." +"Authentication is required to power off the system while an application is " +"inhibiting this." msgstr "" "Потрібна автентифікація, щоб вимкнути систему, коли програми намагаються " "перешкодити цьому." @@ -395,8 +395,8 @@ msgstr "Перезавантажити, якщо інші користувачі #: src/login/org.freedesktop.login1.policy:203 msgid "" -"Authentication is required to reboot the system while other users are " -"logged in." +"Authentication is required to reboot the system while other users are logged " +"in." msgstr "" "Потрібна автентифікація, щоб перезапустити систему, коли інші користувачі в " "ній." @@ -407,8 +407,8 @@ msgstr "Перезапустити систему, коли програми н #: src/login/org.freedesktop.login1.policy:214 msgid "" -"Authentication is required to reboot the system while an application " -"is inhibiting this." +"Authentication is required to reboot the system while an application is " +"inhibiting this." msgstr "" "Потрібна автентифікація, щоб перезапустити систему, коли програми " "намагаються перешкодити цьому." @@ -427,8 +427,8 @@ msgstr "Зупинити систему, коли інші користувач #: src/login/org.freedesktop.login1.policy:236 msgid "" -"Authentication is required to halt the system while other users are " -"logged in." +"Authentication is required to halt the system while other users are logged " +"in." msgstr "" "Потрібна автентифікація, щоб зупинити систему, коли інші користувачі в ній." @@ -438,10 +438,11 @@ msgstr "Зупинити систему, коли програми намага #: src/login/org.freedesktop.login1.policy:247 msgid "" -"Authentication is required to halt the system while an application asked " -"to inhibit it." +"Authentication is required to halt the system while an application is " +"inhibiting this." msgstr "" -"Потрібна автентифікація, щоб зупинити систему, коли програми намагаються " +"Потрібна автентифікація, щоб зупинити систему, коли програма" +" намагається " "перешкодити цьому." #: src/login/org.freedesktop.login1.policy:257 @@ -470,8 +471,8 @@ msgstr "Призупинити систему, коли програми нам #: src/login/org.freedesktop.login1.policy:279 msgid "" -"Authentication is required to suspend the system while an application " -"is inhibiting this." +"Authentication is required to suspend the system while an application is " +"inhibiting this." msgstr "" "Потрібна автентифікація, щоб призупинити систему, коли програми намагаються " "перешкодити цьому." @@ -501,8 +502,8 @@ msgstr "Приспати систему, коли програми намага #: src/login/org.freedesktop.login1.policy:311 msgid "" -"Authentication is required to hibernate the system while an application " -"is inhibiting this." +"Authentication is required to hibernate the system while an application is " +"inhibiting this." msgstr "" "Потрібна автентифікація, щоб приспати систему, коли програми намагаються " "перешкодити цьому." @@ -512,8 +513,7 @@ msgid "Manage active sessions, users and seats" msgstr "Керувати сеансами, користувачами і робочими місцями" #: src/login/org.freedesktop.login1.policy:322 -msgid "" -"Authentication is required to manage active sessions, users and seats." +msgid "Authentication is required to manage active sessions, users and seats." msgstr "" "Потрібна автентифікація, щоб керувати сеансами, користувачами і робочими " "місцями." @@ -767,26 +767,35 @@ 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:144 +#: src/network/org.freedesktop.network1.policy:155 msgid "Authentication is required to renew dynamic addresses." msgstr "Для оновлення динамічних адрес слід пройти розпізнавання." -#: src/network/org.freedesktop.network1.policy:154 +#: src/network/org.freedesktop.network1.policy:165 msgid "Reload network settings" msgstr "Перезавантаження параметрів мережі" -#: src/network/org.freedesktop.network1.policy:155 +#: src/network/org.freedesktop.network1.policy:166 msgid "Authentication is required to reload network settings." msgstr "Для перезавантаження параметрів мережі слід пройти розпізнавання." -#: src/network/org.freedesktop.network1.policy:165 +#: src/network/org.freedesktop.network1.policy:176 msgid "Reconfigure network interface" msgstr "Переналаштування інтерфейсу мережі" -#: src/network/org.freedesktop.network1.policy:166 +#: src/network/org.freedesktop.network1.policy:177 msgid "Authentication is required to reconfigure network interface." msgstr "Для зміни налаштувань інтерфейсу мережі слід пройти розпізнавання." @@ -842,8 +851,8 @@ msgstr "Повернення до початкових параметрів ви #: src/resolve/org.freedesktop.resolve1.policy:133 msgid "Authentication is required to reset name resolution settings." msgstr "" -"Для повернення до початкових параметрів визначення назв вузлів за адресами" -" слід пройти розпізнавання." +"Для повернення до початкових параметрів визначення назв вузлів за адресами " +"слід пройти розпізнавання." #: src/timedate/org.freedesktop.timedate1.policy:22 msgid "Set system time" @@ -885,38 +894,38 @@ msgstr "" "Потрібна автентифікація, щоб контролювати, чи синхронізування часу через " "мережу запущено." -#: src/core/dbus-unit.c:355 +#: src/core/dbus-unit.c:356 msgid "Authentication is required to start '$(unit)'." msgstr "Потрібна автентифікація, щоб запустити «$(unit)»." -#: src/core/dbus-unit.c:356 +#: src/core/dbus-unit.c:357 msgid "Authentication is required to stop '$(unit)'." msgstr "Потрібна автентифікація, щоб зупинити «$(unit)»." -#: src/core/dbus-unit.c:357 +#: src/core/dbus-unit.c:358 msgid "Authentication is required to reload '$(unit)'." msgstr "Потрібна автентифікація, щоб перезавантажити «$(unit)»." -#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359 +#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360 msgid "Authentication is required to restart '$(unit)'." msgstr "Потрібна автентифікація, щоб перезапустити «$(unit)»." -#: src/core/dbus-unit.c:531 +#: src/core/dbus-unit.c:532 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." msgstr "" "Потрібна автентифікація, щоб надіслати сигнал UNIX до процесів «$(unit)»." -#: src/core/dbus-unit.c:562 +#: src/core/dbus-unit.c:563 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "Потрібна автентифікація, щоб скинути «пошкоджений» стан з «$(unit)»." -#: src/core/dbus-unit.c:595 +#: src/core/dbus-unit.c:596 msgid "Authentication is required to set properties on '$(unit)'." msgstr "Потрібна автентифікація, щоб вказати властивості на «$(unit)»." -#: src/core/dbus-unit.c:704 +#: src/core/dbus-unit.c:705 msgid "" "Authentication is required to delete files and directories associated with " "'$(unit)'." diff --git a/po/zh_CN.po b/po/zh_CN.po index 7196cc50b..2655f62c6 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -64,15 +64,15 @@ msgid "Authentication is required to reload the systemd state." msgstr "重新载入 systemd 状态需要认证。" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1 -msgid "Set host name" +msgid "Set hostname" msgstr "设置主机名" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "设置本地主机名需要认证。" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3 -msgid "Set static host name" +msgid "Set static hostname" msgstr "设置静态主机名" # For pretty hostname, the zh_CN/zh_TW translation should be discussed again. @@ -81,8 +81,8 @@ msgstr "设置静态主机名" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4 #, fuzzy msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "设置静态本地主机名或美观主机名需要认证。" #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5 diff --git a/po/zh_TW.po b/po/zh_TW.po index d14a338e5..ac6cd99ef 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -62,21 +62,21 @@ msgid "Authentication is required to reload the systemd state." msgstr "重新載入 systemd 狀態需要驗證。" #: src/hostname/org.freedesktop.hostname1.policy:20 -msgid "Set host name" +msgid "Set hostname" msgstr "設定主機名稱" #: src/hostname/org.freedesktop.hostname1.policy:21 -msgid "Authentication is required to set the local host name." +msgid "Authentication is required to set the local hostname." msgstr "設定主機名稱需要驗證。" #: src/hostname/org.freedesktop.hostname1.policy:30 -msgid "Set static host name" +msgid "Set static hostname" msgstr "設定靜態主機名稱" #: src/hostname/org.freedesktop.hostname1.policy:31 msgid "" -"Authentication is required to set the statically configured local host name, " -"as well as the pretty host name." +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." msgstr "設定靜態預先設定或 pretty 本地主機名稱需要身份驗證。" #: src/hostname/org.freedesktop.hostname1.policy:41 diff --git a/presets/90-systemd.preset b/presets/90-systemd.preset index dc9d02f0b..dd268ae1b 100644 --- a/presets/90-systemd.preset +++ b/presets/90-systemd.preset @@ -19,6 +19,9 @@ enable getty@.service enable systemd-timesyncd.service enable systemd-networkd.service enable systemd-resolved.service +enable systemd-homed.service +enable systemd-userdbd.socket +enable systemd-pstore.service disable console-getty.service disable debug-shell.service @@ -30,25 +33,13 @@ enable reboot.target disable rescue.target disable exit.target +disable systemd-networkd-wait-online.service +disable systemd-time-wait-sync.service +disable systemd-boot-check-no-failures.service +disable systemd-network-generator.service + disable syslog.socket disable systemd-journal-gatewayd.* disable systemd-journal-remote.* disable systemd-journal-upload.* - -enable systemd-pstore.service - -# Passive targets: always off by default, since they should only be pulled in -# by dependent units. - -disable cryptsetup-pre.target -disable getty-pre.target -disable local-fs-pre.target -disable network.target -disable network-pre.target -disable nss-lookup.target -disable nss-user-lookup.target -disable remote-fs-pre.target -disable rpcbind.target -disable time-set.target -disable time-sync.target diff --git a/presets/user/90-systemd.preset b/presets/user/90-systemd.preset index fd402c8c1..5ee969090 100644 --- a/presets/user/90-systemd.preset +++ b/presets/user/90-systemd.preset @@ -13,9 +13,3 @@ enable systemd-tmpfiles-setup.service enable systemd-tmpfiles-clean.timer - -# Passive targets: always off by default, since they should only be pulled in -# by dependent units. - -disable graphical-session-pre.target -disable graphical-session.target diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in index 50747a198..cef78f9d6 100644 --- a/rules.d/50-udev-default.rules.in +++ b/rules.d/50-udev-default.rules.in @@ -10,8 +10,9 @@ SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio- SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" 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" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb", GOTO="default_hwdb_imported" ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" +LABEL="default_hwdb_imported" ACTION!="add", GOTO="default_end" diff --git a/rules.d/60-autosuspend.rules b/rules.d/60-autosuspend.rules new file mode 100644 index 000000000..1f9ebef63 --- /dev/null +++ b/rules.d/60-autosuspend.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add", GOTO="autosuspend_end" + +# I2C rules +SUBSYSTEM=="i2c", ATTR{name}=="cyapa", \ + ATTR{power/control}="on", GOTO="autosuspend_end" + +# Enable autosuspend if hwdb says so. Here we are relying on +# the hwdb import done earlier based on MODALIAS. +ENV{ID_AUTOSUSPEND}=="1", TEST=="power/control", \ + ATTR{power/control}="auto" + +LABEL="autosuspend_end" diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules index 90847927e..fc7f733e2 100644 --- a/rules.d/60-persistent-storage.rules +++ b/rules.d/60-persistent-storage.rules @@ -94,6 +94,9 @@ ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n" KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" +# compatible links for ATA devices +KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}" +ENV{DEVTYPE}=="partition", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}-part%n" # legacy virtio-pci by-path links (deprecated) KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}" diff --git a/rules.d/60-serial.rules b/rules.d/60-serial.rules index f303e27fd..b1626650b 100644 --- a/rules.d/60-serial.rules +++ b/rules.d/60-serial.rules @@ -4,8 +4,9 @@ ACTION=="remove", GOTO="serial_end" SUBSYSTEM!="tty", GOTO="serial_end" SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" -SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" +# We already ran the hwdb builtin for devices with MODALIAS in 50-default.rules. +# Let's cover the remaining case here, where we walk up the tree to find a node with $MODALIAS. +ENV{MODALIAS}=="", SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" diff --git a/rules.d/61-autosuspend-manual.rules b/rules.d/61-autosuspend-manual.rules deleted file mode 100644 index 5a16727b5..000000000 --- a/rules.d/61-autosuspend-manual.rules +++ /dev/null @@ -1,21 +0,0 @@ -# This udev rule is for any devices that should enter automatic suspend -# but are not already included in generated rules from Chromium OS via -# tools/make-autosuspend-rules.py -# - -ACTION!="add", GOTO="autosuspend_manual_end" -SUBSYSTEM!="usb", GOTO="autosuspend_manual_end" - -SUBSYSTEM=="usb", GOTO="autosuspend_manual_usb" - -# USB rules -LABEL="autosuspend_manual_usb" -ATTR{idVendor}=="056a", ATTR{idProduct}=="51a0", GOTO="autosuspend_manual_enable" -ATTR{idVendor}=="058f", ATTR{idProduct}=="9540", GOTO="autosuspend_manual_enable" -GOTO="autosuspend_manual_end" - -# Enable autosuspend -LABEL="autosuspend_manual_enable" -TEST=="power/control", ATTR{power/control}="auto", GOTO="autosuspend_manual_end" - -LABEL="autosuspend_manual_end" diff --git a/rules.d/meson.build b/rules.d/meson.build index 13d1d330c..ca4445d77 100644 --- a/rules.d/meson.build +++ b/rules.d/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1+ rules = files(''' + 60-autosuspend.rules 60-block.rules 60-cdrom_id.rules 60-drm.rules @@ -14,7 +15,6 @@ rules = files(''' 60-persistent-v4l.rules 60-sensor.rules 60-serial.rules - 61-autosuspend-manual.rules 70-joystick.rules 70-mouse.rules 70-touchpad.rules @@ -45,11 +45,3 @@ foreach file : rules_in install_dir : udevrulesdir) all_rules += gen endforeach - -auto_suspend_rules = custom_target( - '60-autosuspend-chromiumos.rules', - output : '60-autosuspend-chromiumos.rules', - command : make_autosuspend_rules_py, - capture : true, - install : true, - install_dir: [udevrulesdir]) diff --git a/semaphoreci/semaphore-runner.sh b/semaphoreci/semaphore-runner.sh index 2bf8ce0d5..abffb2058 100755 --- a/semaphoreci/semaphore-runner.sh +++ b/semaphoreci/semaphore-runner.sh @@ -4,8 +4,8 @@ set -eux # default to Debian testing DISTRO=${DISTRO:-debian} -RELEASE=${RELEASE:-buster} -BRANCH=${BRANCH:-debian/master} +RELEASE=${RELEASE:-bullseye} +BRANCH=${BRANCH:-upstream-ci} ARCH=${ARCH:-amd64} CONTAINER=${RELEASE}-${ARCH} CACHE_DIR=${SEMAPHORE_CACHE_DIR:=/tmp} @@ -13,11 +13,12 @@ AUTOPKGTEST_DIR="${CACHE_DIR}/autopkgtest" # semaphore cannot expose these, but useful for interactive/local runs ARTIFACTS_DIR=/tmp/artifacts PHASES=(${@:-SETUP RUN}) +UBUNTU_RELEASE="$(lsb_release -cs)" create_container() { # create autopkgtest LXC image; this sometimes fails with "Unable to fetch # GPG key from keyserver", so retry a few times - for retry in $(seq 5); do + for retry in {1..5}; do sudo lxc-create -n $CONTAINER -t download -- -d $DISTRO -r $RELEASE -a $ARCH --keyserver hkp://keyserver.ubuntu.com:80 && break sleep $((retry*retry)) done @@ -36,7 +37,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 libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev +apt-get install -y fdisk tree libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev apt-get purge --auto-remove -y unattended-upgrades systemctl unmask systemd-networkd systemctl enable systemd-networkd @@ -51,9 +52,9 @@ for phase in "${PHASES[@]}"; do sudo rm -f /etc/apt/sources.list.d/* # enable backports for latest LXC - echo 'deb http://archive.ubuntu.com/ubuntu xenial-backports main restricted universe multiverse' | sudo tee -a /etc/apt/sources.list.d/backports.list + echo "deb http://archive.ubuntu.com/ubuntu $UBUNTU_RELEASE-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/backports.list sudo apt-get -q update - sudo apt-get install -y -t xenial-backports lxc + sudo apt-get install -y -t "$UBUNTU_RELEASE-backports" lxc sudo apt-get install -y python3-debian git dpkg-dev fakeroot [ -d $AUTOPKGTEST_DIR ] || git clone --quiet --depth=1 https://salsa.debian.org/ci-team/autopkgtest.git "$AUTOPKGTEST_DIR" @@ -66,9 +67,9 @@ for phase in "${PHASES[@]}"; do git checkout FETCH_HEAD debian # craft changelog - UPSTREAM_VER=$(git describe | sed 's/^v//') + UPSTREAM_VER=$(git describe | sed 's/^v//;s/-/./g') cat << EOF > debian/changelog.new -systemd (${UPSTREAM_VER}-0) UNRELEASED; urgency=low +systemd (${UPSTREAM_VER}.0) UNRELEASED; urgency=low * Automatic build for upstream test diff --git a/shell-completion/bash/bootctl b/shell-completion/bash/bootctl index 9c3189f89..9fc6cb3df 100644 --- a/shell-completion/bash/bootctl +++ b/shell-completion/bash/bootctl @@ -59,6 +59,7 @@ _bootctl() { # systemd-efi-options takes an argument, but it is free-form, so we cannot complete it [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list' [BOOTENTRY]='set-default set-oneshot' + [BOOLEAN]='reboot-to-firmware' ) for ((i=0; i < COMP_CWORD; i++)); do diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index f81dafba8..fdbe32e5f 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -7,7 +7,7 @@ __systemctl() { local mode=$1; shift 1 - systemctl $mode --full --no-legend --no-pager "$@" 2>/dev/null + systemctl $mode --full --no-legend --no-pager --plain "$@" 2>/dev/null } __systemd_properties() { diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index 1b4f1b0d1..0c61f5450 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -31,7 +31,7 @@ __get_machines() { } __get_services() { - systemctl list-units --no-legend --no-pager -t service --all $1 | \ + systemctl list-units --no-legend --no-pager --plain -t service --all $1 | \ { while read -r a b c; do [[ $b == "loaded" ]]; echo " $a"; done } } diff --git a/shell-completion/bash/systemd-cgls b/shell-completion/bash/systemd-cgls index 10f6b38fc..ae41f8ba5 100644 --- a/shell-completion/bash/systemd-cgls +++ b/shell-completion/bash/systemd-cgls @@ -29,7 +29,7 @@ __get_machines() { } __get_units_have_cgroup() { - systemctl $1 list-units | { + systemctl $1 --full --no-legend --no-pager --plain list-units | { while read -r a b c d; do [[ $c == "active" && ${a##*.} =~ (service|socket|mount|swap|slice|scope) ]] && echo " $a" done diff --git a/shell-completion/bash/systemd-nspawn b/shell-completion/bash/systemd-nspawn index d263fd5dd..a8bd406fb 100644 --- a/shell-completion/bash/systemd-nspawn +++ b/shell-completion/bash/systemd-nspawn @@ -30,7 +30,7 @@ __get_users() { __get_slices() { local a b - systemctl list-units -t slice --no-legend --no-pager | { while read a b; do echo " $a"; done; }; + systemctl list-units -t slice --no-legend --no-pager --plain | { while read a b; do echo " $a"; done; }; } __get_machines() { @@ -71,7 +71,7 @@ _systemd_nspawn() { --pivot-root --property --private-users --network-namespace-path --network-ipvlan --network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity - --resolv-conf --timezone' + --resolv-conf --timezone --root-hash-sig' ) _init_completion || return @@ -183,6 +183,10 @@ _systemd_nspawn() { --timezone) comps=$( systemd-nspawn --timezone=help 2>/dev/null ) ;; + --root-hash-sig) + compopt -o nospace + comps=$( compgen -A file -- "$cur" ) + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run index 71692aa19..88f4443ad 100644 --- a/shell-completion/bash/systemd-run +++ b/shell-completion/bash/systemd-run @@ -18,7 +18,7 @@ __systemctl() { local mode=$1; shift 1 - systemctl $mode --full --no-legend "$@" + systemctl $mode --full --no-legend --no-pager --plain "$@" } __get_slice_units () { __systemctl $1 list-units --all -t slice \ diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index cb12c2553..76126a632 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -38,7 +38,7 @@ __get_all_devs() { } __get_all_device_units() { - systemctl list-units -t device --full --no-legend --no-pager 2>/dev/null | \ + systemctl list-units -t device --full --no-legend --no-pager --plain 2>/dev/null | \ { while read -r a b; do echo "$a"; done; } } diff --git a/shell-completion/zsh/_bootctl b/shell-completion/zsh/_bootctl index 6a903c562..fc051bd87 100644 --- a/shell-completion/zsh/_bootctl +++ b/shell-completion/zsh/_bootctl @@ -42,6 +42,7 @@ _bootctl_reboot-to-firmware() { "is-installed:Test whether systemd-boot is installed in the ESP" "random-seed:Initialize random seed in ESP and EFI variables" "systemd-efi-options:Query or set system options string in EFI variable" + "reboot-to-firmware:Query or set reboot-to-firmware EFI flag" "list:List boot loader entries" "set-default:Set the default boot loader entry" "set-oneshot:Set the default boot loader entry only for the next boot" diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index 1b6f9f083..582d469c3 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -145,7 +145,7 @@ # @todo _systemd-run has a helper with the same name, so we must redefine __systemctl() { - systemctl $_sys_service_mgr --full --no-legend --no-pager "$@" 2>/dev/null + systemctl $_sys_service_mgr --full --no-legend --no-pager --plain "$@" 2>/dev/null } diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run index ca0faa148..22b82d66f 100644 --- a/shell-completion/zsh/_systemd-run +++ b/shell-completion/zsh/_systemd-run @@ -5,7 +5,7 @@ __systemctl() { local -a _modes _modes=("--user" "--system") - systemctl ${words:*_modes} --full --no-legend --no-pager "$@" 2>/dev/null + systemctl ${words:*_modes} --full --no-legend --no-pager --plain "$@" 2>/dev/null } (( $+functions[__systemd-run_get_slices] )) || diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c index d0cefa099..52ad38263 100644 --- a/src/analyze/analyze-condition.c +++ b/src/analyze/analyze-condition.c @@ -21,6 +21,7 @@ static const condition_definition condition_definitions[] = { { "ConditionPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK }, { "ConditionPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT }, { "ConditionPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE }, + { "ConditionPathIsEncrypted", config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED }, { "ConditionDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY }, { "ConditionFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY }, { "ConditionFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE }, @@ -44,6 +45,7 @@ static const condition_definition condition_definitions[] = { { "AssertPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK }, { "AssertPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT }, { "AssertPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE }, + { "AssertPathIsEncrypted", config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED }, { "AssertDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY }, { "AssertFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY }, { "AssertFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE }, @@ -83,11 +85,14 @@ static int parse_condition(Unit *u, const char *line) { p = startswith(line, c->name); if (!p) continue; - p += strspn(p, WHITESPACE); - if (*p != '=') - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected \"=\" in \"%s\".", line); - p += 1 + strspn(p + 1, WHITESPACE); + p += strspn(p, WHITESPACE); + + if (*p != '=') + continue; + p++; + + p += strspn(p, WHITESPACE); return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u); } @@ -143,11 +148,11 @@ int verify_conditions(char **lines, UnitFileScope scope) { return r; } - r = condition_test_list(u->asserts, assert_type_to_string, log_helper, u); + r = condition_test_list(u->asserts, environ, assert_type_to_string, log_helper, u); if (u->asserts) log_notice("Asserts %s.", r > 0 ? "succeeded" : "failed"); - q = condition_test_list(u->conditions, condition_type_to_string, log_helper, u); + q = condition_test_list(u->conditions, environ, condition_type_to_string, log_helper, u); if (u->conditions) log_notice("Conditions %s.", q > 0 ? "succeeded" : "failed"); diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index 5e7756bff..d4996c3c6 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -4,6 +4,7 @@ #include "analyze-security.h" #include "bus-error.h" +#include "bus-map-properties.h" #include "bus-unit-util.h" #include "bus-util.h" #include "env-util.h" @@ -91,7 +92,7 @@ struct security_info { char **system_call_architectures; - bool system_call_filter_whitelist; + bool system_call_filter_allow_list; Set *system_call_filter; uint32_t _umask; @@ -141,7 +142,7 @@ static void security_info_free(struct security_info *i) { strv_free(i->supplementary_groups); strv_free(i->system_call_architectures); - set_free_free(i->system_call_filter); + set_free(i->system_call_filter); } static bool security_info_runs_privileged(const struct security_info *i) { @@ -492,7 +493,7 @@ static int assess_system_call_architectures( #if HAVE_SECCOMP -static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterSet *f) { +static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f) { const char *syscall; NULSTR_FOREACH(syscall, f->value) { @@ -502,7 +503,7 @@ static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterS const SyscallFilterSet *g; assert_se(g = syscall_filter_set_find(syscall)); - if (syscall_names_in_filter(s, whitelist, g)) + if (syscall_names_in_filter(s, allow_list, g)) return true; /* bad! */ continue; @@ -513,7 +514,7 @@ static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterS if (id < 0) continue; - if (set_contains(s, syscall) == whitelist) { + if (set_contains(s, syscall) == allow_list) { log_debug("Offending syscall filter item: %s", syscall); return true; /* bad! */ } @@ -541,30 +542,30 @@ static int assess_system_call_filter( assert(a->parameter < _SYSCALL_FILTER_SET_MAX); f = syscall_filter_sets + a->parameter; - if (!info->system_call_filter_whitelist && set_isempty(info->system_call_filter)) { + if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) { d = strdup("Service does not filter system calls"); b = 10; } else { bool bad; log_debug("Analyzing system call filter, checking against: %s", f->name); - bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_whitelist, f); + bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f); log_debug("Result: %s", bad ? "bad" : "good"); - if (info->system_call_filter_whitelist) { + if (info->system_call_filter_allow_list) { if (bad) { - (void) asprintf(&d, "System call whitelist defined for service, and %s is included", f->name); + (void) asprintf(&d, "System call allow list defined for service, and %s is included", f->name); b = 9; } else { - (void) asprintf(&d, "System call whitelist defined for service, and %s is not included", f->name); + (void) asprintf(&d, "System call allow list defined for service, and %s is not included", f->name); b = 0; } } else { if (bad) { - (void) asprintf(&d, "System call blacklist defined for service, and %s is not included", f->name); + (void) asprintf(&d, "System call deny list defined for service, and %s is not included", f->name); b = 10; } else { - (void) asprintf(&d, "System call blacklist defined for service, and %s is included", f->name); + (void) asprintf(&d, "System call deny list defined for service, and %s is included", f->name); b = 5; } } @@ -599,13 +600,13 @@ static int assess_ip_address_allow( d = strdup("Service defines custom ingress/egress IP filters with BPF programs"); b = 0; } else if (!info->ip_address_deny_all) { - d = strdup("Service does not define an IP address whitelist"); + d = strdup("Service does not define an IP address allow list"); b = 10; } else if (info->ip_address_allow_other) { - d = strdup("Service defines IP address whitelist with non-localhost entries"); + d = strdup("Service defines IP address allow list with non-localhost entries"); b = 5; } else if (info->ip_address_allow_localhost) { - d = strdup("Service defines IP address whitelist with only localhost entries"); + d = strdup("Service defines IP address allow list with only localhost entries"); b = 2; } else { d = strdup("Service blocks all IP address ranges"); @@ -1639,7 +1640,7 @@ static int property_read_restrict_address_families( void *userdata) { struct security_info *info = userdata; - int whitelist, r; + int allow_list, r; assert(bus); assert(member); @@ -1649,7 +1650,7 @@ static int property_read_restrict_address_families( if (r < 0) return r; - r = sd_bus_message_read(m, "b", &whitelist); + r = sd_bus_message_read(m, "b", &allow_list); if (r < 0) return r; @@ -1657,7 +1658,7 @@ static int property_read_restrict_address_families( info->restrict_address_family_unix = info->restrict_address_family_netlink = info->restrict_address_family_packet = - info->restrict_address_family_other = whitelist; + info->restrict_address_family_other = allow_list; r = sd_bus_message_enter_container(m, 'a', "s"); if (r < 0) @@ -1673,15 +1674,15 @@ static int property_read_restrict_address_families( break; if (STR_IN_SET(name, "AF_INET", "AF_INET6")) - info->restrict_address_family_inet = !whitelist; + info->restrict_address_family_inet = !allow_list; else if (streq(name, "AF_UNIX")) - info->restrict_address_family_unix = !whitelist; + info->restrict_address_family_unix = !allow_list; else if (streq(name, "AF_NETLINK")) - info->restrict_address_family_netlink = !whitelist; + info->restrict_address_family_netlink = !allow_list; else if (streq(name, "AF_PACKET")) - info->restrict_address_family_packet = !whitelist; + info->restrict_address_family_packet = !allow_list; else - info->restrict_address_family_other = !whitelist; + info->restrict_address_family_other = !allow_list; } r = sd_bus_message_exit_container(m); @@ -1699,7 +1700,7 @@ static int property_read_system_call_filter( void *userdata) { struct security_info *info = userdata; - int whitelist, r; + int allow_list, r; assert(bus); assert(member); @@ -1709,11 +1710,11 @@ static int property_read_system_call_filter( if (r < 0) return r; - r = sd_bus_message_read(m, "b", &whitelist); + r = sd_bus_message_read(m, "b", &allow_list); if (r < 0) return r; - info->system_call_filter_whitelist = whitelist; + info->system_call_filter_allow_list = allow_list; r = sd_bus_message_enter_container(m, 'a', "s"); if (r < 0) @@ -1728,11 +1729,7 @@ static int property_read_system_call_filter( if (r == 0) break; - r = set_ensure_allocated(&info->system_call_filter, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(info->system_call_filter, name); + r = set_put_strdup(&info->system_call_filter, name); if (r < 0) return r; } diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 8275360ad..30cb79d50 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -94,6 +94,7 @@ static int generate_path(char **var, char **filenames) { } static int verify_socket(Unit *u) { + Unit *service; int r; assert(u); @@ -101,26 +102,15 @@ static int verify_socket(Unit *u) { if (u->type != UNIT_SOCKET) return 0; - /* Cannot run this without the service being around */ - - /* This makes sure instance is created if necessary. */ - r = socket_instantiate_service(SOCKET(u)); + r = socket_load_service_unit(SOCKET(u), -1, &service); if (r < 0) - return log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m"); + return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); - /* This checks both type of sockets */ - if (UNIT_ISSET(SOCKET(u)->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); - log_unit_debug(u, "Using %s", UNIT(service)->id); - - if (UNIT(service)->load_state != UNIT_LOADED) { - log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id); - return -ENOENT; - } - } + 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; } diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 3ea9041c1..9d64e3c76 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -17,8 +17,9 @@ #include "analyze-verify.h" #include "build.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "calendarspec.h" #include "conf-files.h" #include "copy.h" @@ -349,14 +350,7 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, &reply, - NULL); + 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)); @@ -633,7 +627,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, &use_full_bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); n = acquire_boot_times(bus, &boot); if (n < 0) @@ -1032,7 +1026,7 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); n = acquire_time_data(bus, ×); if (n <= 0) @@ -1075,7 +1069,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); n = acquire_time_data(bus, ×); if (n <= 0) @@ -1132,7 +1126,7 @@ static int analyze_time(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); r = pretty_boot_time(bus, &buf); if (r < 0) @@ -1270,7 +1264,7 @@ static int dot(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); if (r < 0) @@ -1284,15 +1278,7 @@ static int dot(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, - &reply, - ""); + r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, ""); if (r < 0) log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); @@ -1334,15 +1320,7 @@ static int dump_fallback(sd_bus *bus) { assert(bus); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Dump", - &error, - &reply, - NULL); + 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)); @@ -1363,22 +1341,14 @@ static int dump(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); (void) pager_open(arg_pager_flags); if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD)) return dump_fallback(bus); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "DumpByFileDescriptor", - &error, - &reply, - NULL); + 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)) @@ -1442,17 +1412,9 @@ static int set_log_level(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogLevel", - &error, - "s", - argv[1]); + r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); @@ -1467,16 +1429,9 @@ static int get_log_level(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogLevel", - &error, - &level); + r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level); if (r < 0) return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r)); @@ -1498,17 +1453,9 @@ static int set_log_target(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogTarget", - &error, - "s", - argv[1]); + r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); @@ -1523,16 +1470,9 @@ static int get_log_target(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogTarget", - &error, - &target); + r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target); if (r < 0) return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r)); @@ -1655,7 +1595,7 @@ static int dump_exit_status(int argc, char *argv[], void *userdata) { #if HAVE_SECCOMP static int load_kernel_syscalls(Set **ret) { - _cleanup_(set_free_freep) Set *syscalls = NULL; + _cleanup_set_free_ Set *syscalls = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -1691,11 +1631,7 @@ static int load_kernel_syscalls(Set **ret) { if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl")) continue; - r = set_ensure_allocated(&syscalls, &string_hash_ops); - if (r < 0) - return log_oom(); - - r = set_put_strdup(syscalls, e); + r = set_put_strdup(&syscalls, e); if (r < 0) return log_error_errno(r, "Failed to add system call to list: %m"); } @@ -1735,7 +1671,7 @@ static int dump_syscall_filters(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); if (strv_isempty(strv_skip(argv, 1))) { - _cleanup_(set_free_freep) Set *kernel = NULL; + _cleanup_set_free_ Set *kernel = NULL; int i, k; k = load_kernel_syscalls(&kernel); @@ -2117,19 +2053,11 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); if (argc == 1) { /* get ServiceWatchdogs */ - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ServiceWatchdogs", - &error, - 'b', - &b); + 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)); @@ -2141,15 +2069,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) { if (b < 0) return log_error_errno(b, "Failed to parse service-watchdogs argument: %m"); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ServiceWatchdogs", - &error, - "b", - b); + 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)); } @@ -2171,7 +2091,7 @@ static int do_security(int argc, char *argv[], void *userdata) { r = acquire_bus(&bus, NULL); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); (void) pager_open(arg_pager_flags); @@ -2457,9 +2377,7 @@ static int run(int argc, char *argv[]) { setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c index 54431f5d0..5f8212685 100644 --- a/src/basic/blockdev-util.c +++ b/src/basic/blockdev-util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include "alloc-util.h" @@ -187,3 +188,29 @@ int get_block_device_harder(const char *path, dev_t *ret) { return 1; } + +int lock_whole_block_device(dev_t devt, int operation) { + _cleanup_free_ char *whole_node = NULL; + _cleanup_close_ int lock_fd = -1; + dev_t whole_devt; + int r; + + /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */ + + r = block_get_whole_disk(devt, &whole_devt); + if (r < 0) + return r; + + r = device_path_make_major_minor(S_IFBLK, whole_devt, &whole_node); + if (r < 0) + return r; + + lock_fd = open(whole_node, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (lock_fd < 0) + return -errno; + + if (flock(lock_fd, operation) < 0) + return -errno; + + return TAKE_FD(lock_fd); +} diff --git a/src/basic/blockdev-util.h b/src/basic/blockdev-util.h index 6d8a79656..1e7588f71 100644 --- a/src/basic/blockdev-util.h +++ b/src/basic/blockdev-util.h @@ -18,3 +18,5 @@ int block_get_originating(dev_t d, dev_t *ret); int get_block_device(const char *path, dev_t *dev); int get_block_device_harder(const char *path, dev_t *dev); + +int lock_whole_block_device(dev_t devt, int operation); diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 18a721775..71e1bc92e 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -160,6 +160,31 @@ int btrfs_subvol_make(const char *path) { return btrfs_subvol_make_fd(fd, subvolume); } +int btrfs_subvol_make_fallback(const char *path, mode_t mode) { + mode_t old, combined; + int r; + + assert(path); + + /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */ + old = umask(~mode); + combined = old | ~mode; + if (combined != ~mode) + umask(combined); + r = btrfs_subvol_make(path); + umask(old); + + if (r >= 0) + return 1; /* subvol worked */ + if (r != -ENOTTY) + return r; + + if (mkdir(path, mode) < 0) + return -errno; + + return 0; /* plain directory */ +} + int btrfs_subvol_set_read_only_fd(int fd, bool b) { uint64_t flags, nflags; struct stat st; @@ -175,11 +200,7 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) { if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) return -errno; - if (b) - nflags = flags | BTRFS_SUBVOL_RDONLY; - else - nflags = flags & ~BTRFS_SUBVOL_RDONLY; - + nflags = UPDATE_FLAG(flags, BTRFS_SUBVOL_RDONLY, b); if (flags == nflags) return 0; @@ -298,7 +319,7 @@ int btrfs_get_block_device_fd(int fd, dev_t *dev) { return -errno; if (!S_ISBLK(st.st_mode)) - return -ENODEV; + return -ENOTBLK; if (major(st.st_rdev) == 0) return -ENODEV; @@ -906,9 +927,12 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) { for (c = 0;; c++) { if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) { - /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */ - if (errno == EINVAL) - return -ENOPROTOOPT; + /* On old kernels if quota is not enabled, we get EINVAL. On newer kernels we get + * ENOTCONN. Let's always convert this to ENOTCONN to make this recognizable + * everywhere the same way. */ + + if (IN_SET(errno, EINVAL, ENOTCONN)) + return -ENOTCONN; if (errno == EBUSY && c < 10) { (void) btrfs_quota_scan_wait(fd); @@ -1128,7 +1152,6 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { _cleanup_free_ char *p = NULL; const struct btrfs_root_ref *ref; - struct btrfs_ioctl_ino_lookup_args ino_args; btrfs_ioctl_search_args_set(&args, sh); @@ -1143,9 +1166,10 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol if (!p) return -ENOMEM; - zero(ino_args); - ino_args.treeid = subvol_id; - ino_args.objectid = htole64(ref->dirid); + struct btrfs_ioctl_ino_lookup_args ino_args = { + .treeid = subvol_id, + .objectid = htole64(ref->dirid), + }; if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) return -errno; @@ -1483,7 +1507,6 @@ static int subvol_snapshot_children( FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL; - struct btrfs_ioctl_ino_lookup_args ino_args; const struct btrfs_root_ref *ref; _cleanup_close_ int old_child_fd = -1, new_child_fd = -1; @@ -1507,9 +1530,10 @@ static int subvol_snapshot_children( if (!p) return -ENOMEM; - zero(ino_args); - ino_args.treeid = old_subvol_id; - ino_args.objectid = htole64(ref->dirid); + struct btrfs_ioctl_ino_lookup_args ino_args = { + .treeid = old_subvol_id, + .objectid = htole64(ref->dirid), + }; if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) return -errno; diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index b15667bf2..c1bbb42ca 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -66,6 +66,8 @@ int btrfs_quota_scan_ongoing(int fd); int btrfs_subvol_make(const char *path); int btrfs_subvol_make_fd(int fd, const char *subvolume); +int btrfs_subvol_make_fallback(const char *path, mode_t); + int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata); static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) { return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL); diff --git a/src/basic/build.h b/src/basic/build.h index c47e912eb..d160af5bc 100644 --- a/src/basic/build.h +++ b/src/basic/build.h @@ -87,6 +87,12 @@ #define _LZ4_FEATURE_ "-LZ4" #endif +#if HAVE_ZSTD +#define _ZSTD_FEATURE_ "+ZSTD" +#else +#define _ZSTD_FEATURE_ "-ZSTD" +#endif + #if HAVE_SECCOMP #define _SECCOMP_FEATURE_ "+SECCOMP" #else @@ -146,6 +152,7 @@ _ACL_FEATURE_ " " \ _XZ_FEATURE_ " " \ _LZ4_FEATURE_ " " \ + _ZSTD_FEATURE_ " " \ _SECCOMP_FEATURE_ " " \ _BLKID_FEATURE_ " " \ _ELFUTILS_FEATURE_ " " \ diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index af27d84ba..b48383394 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -18,7 +18,6 @@ static const struct capability_name* lookup_capability(register const char *str, #include "cap-to-name.h" const char *capability_to_name(int id) { - if (id < 0) return NULL; @@ -57,12 +56,11 @@ int capability_list_length(void) { int capability_set_to_string_alloc(uint64_t set, char **s) { _cleanup_free_ char *str = NULL; - unsigned long i; size_t allocated = 0, n = 0; assert(s); - for (i = 0; i <= cap_last_cap(); i++) + for (unsigned i = 0; i <= cap_last_cap(); i++) if (set & (UINT64_C(1) << i)) { const char *p; char buf[2 + 16 + 1]; @@ -70,7 +68,7 @@ int capability_set_to_string_alloc(uint64_t set, char **s) { p = capability_to_name(i); if (!p) { - xsprintf(buf, "0x%lx", i); + xsprintf(buf, "0x%x", i); p = buf; } @@ -95,11 +93,10 @@ int capability_set_to_string_alloc(uint64_t set, char **s) { int capability_set_from_string(const char *s, uint64_t *set) { uint64_t val = 0; - const char *p; assert(set); - for (p = s;;) { + for (const char *p = s;;) { _cleanup_free_ char *word = NULL; int r; diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index ac96eabc0..5a4d020f5 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -31,8 +31,8 @@ int have_effective_cap(int value) { return fv == CAP_SET; } -unsigned long cap_last_cap(void) { - static thread_local unsigned long saved; +unsigned cap_last_cap(void) { + static thread_local unsigned saved; static thread_local bool valid = false; _cleanup_free_ char *content = NULL; unsigned long p = 0; @@ -65,7 +65,7 @@ unsigned long cap_last_cap(void) { if (prctl(PR_CAPBSET_READ, p) < 0) { /* Hmm, look downwards, until we find one that works */ - for (p--; p > 0; p --) + for (p--; p > 0; p--) if (prctl(PR_CAPBSET_READ, p) >= 0) break; @@ -84,12 +84,10 @@ unsigned long cap_last_cap(void) { } int capability_update_inherited_set(cap_t caps, uint64_t set) { - unsigned long i; - /* Add capabilities in the set to the inherited caps, drops capabilities not in the set. * Do not apply them yet. */ - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR; cap_value_t v; @@ -104,11 +102,10 @@ int capability_update_inherited_set(cap_t caps, uint64_t set) { int capability_ambient_set_apply(uint64_t set, bool also_inherit) { _cleanup_cap_free_ cap_t caps = NULL; - unsigned long i; int r; /* Remove capabilities requested in ambient set, but not in the bounding set */ - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { if (set == 0) break; @@ -140,7 +137,7 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { return -errno; } - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { if (set & (UINT64_C(1) << i)) { @@ -167,7 +164,6 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { int capability_bounding_set_drop(uint64_t keep, bool right_now) { _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL; cap_flag_value_t fv; - unsigned long i; int r; /* If we are run as PID 1 we will lack CAP_SETPCAP by default @@ -204,7 +200,7 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { if (!after_cap) return -errno; - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { cap_value_t v; if ((keep & (UINT64_C(1) << i))) @@ -390,7 +386,6 @@ bool ambient_capabilities_supported(void) { } bool capability_quintet_mangle(CapabilityQuintet *q) { - unsigned long i; uint64_t combined, drop = 0; bool ambient_supported; @@ -402,7 +397,7 @@ bool capability_quintet_mangle(CapabilityQuintet *q) { if (ambient_supported) combined |= q->ambient; - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { unsigned long bit = UINT64_C(1) << i; if (!FLAGS_SET(combined, bit)) continue; @@ -431,16 +426,15 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { int r; if (q->ambient != (uint64_t) -1) { - unsigned long i; bool changed = false; c = cap_get_proc(); if (!c) return -errno; - /* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted - * cap */ - for (i = 0; i <= cap_last_cap(); i++) { + /* In order to raise the ambient caps set we first need to raise the matching + * inheritable + permitted cap */ + for (unsigned i = 0; i <= cap_last_cap(); i++) { uint64_t m = UINT64_C(1) << i; cap_value_t cv = (cap_value_t) i; cap_flag_value_t old_value_inheritable, old_value_permitted; @@ -475,7 +469,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { if (q->inheritable != (uint64_t) -1 || q->permitted != (uint64_t) -1 || q->effective != (uint64_t) -1) { bool changed = false; - unsigned long i; if (!c) { c = cap_get_proc(); @@ -483,7 +476,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { return -errno; } - for (i = 0; i <= cap_last_cap(); i++) { + for (unsigned i = 0; i <= cap_last_cap(); i++) { uint64_t m = UINT64_C(1) << i; cap_value_t cv = (cap_value_t) i; diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index b5bce29ab..fcc59daed 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -12,7 +12,7 @@ #define CAP_ALL (uint64_t) -1 -unsigned long cap_last_cap(void); +unsigned cap_last_cap(void); int have_effective_cap(int value); int capability_bounding_set_drop(uint64_t keep, bool right_now); int capability_bounding_set_drop_usermode(uint64_t keep); diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 752133a8d..e94fcfad0 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -37,6 +37,7 @@ #include "strv.h" #include "unit-name.h" #include "user-util.h" +#include "xattr-util.h" static int cg_enumerate_items(const char *controller, const char *path, FILE **_f, const char *item) { _cleanup_free_ char *fs = NULL; @@ -148,6 +149,17 @@ bool cg_ns_supported(void) { return enabled; } +bool cg_freezer_supported(void) { + static thread_local int supported = -1; + + if (supported >= 0) + return supported; + + supported = cg_all_unified() > 0 && access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) == 0; + + return supported; +} + int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { _cleanup_free_ char *fs = NULL; int r; @@ -605,6 +617,24 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi return (int) n; } +int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret) { + _cleanup_free_ char *fs = NULL; + int r; + + assert(path); + assert(name); + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) + return r; + + r = getxattr_malloc(fs, name, ret, false); + if (r < 0) + return r; + + return r; +} + int cg_remove_xattr(const char *controller, const char *path, const char *name) { _cleanup_free_ char *fs = NULL; int r; @@ -878,9 +908,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) { } } -int cg_split_spec(const char *spec, char **controller, char **path) { - char *t = NULL, *u = NULL; - const char *e; +int cg_split_spec(const char *spec, char **ret_controller, char **ret_path) { + _cleanup_free_ char *controller = NULL, *path = NULL; assert(spec); @@ -888,76 +917,53 @@ int cg_split_spec(const char *spec, char **controller, char **path) { if (!path_is_normalized(spec)) return -EINVAL; - if (path) { - t = strdup(spec); - if (!t) + if (ret_path) { + path = strdup(spec); + if (!path) return -ENOMEM; - *path = path_simplify(t, false); + path_simplify(path, false); } - if (controller) - *controller = NULL; + } else { + const char *e; - return 0; - } - - e = strchr(spec, ':'); - if (!e) { - if (!cg_controller_is_valid(spec)) - return -EINVAL; - - if (controller) { - t = strdup(spec); - if (!t) + e = strchr(spec, ':'); + if (e) { + controller = strndup(spec, e-spec); + if (!controller) return -ENOMEM; + if (!cg_controller_is_valid(controller)) + return -EINVAL; - *controller = t; + if (!isempty(e + 1)) { + path = strdup(e+1); + if (!path) + return -ENOMEM; + + if (!path_is_normalized(path) || + !path_is_absolute(path)) + return -EINVAL; + + path_simplify(path, false); + } + + } else { + if (!cg_controller_is_valid(spec)) + return -EINVAL; + + if (ret_controller) { + controller = strdup(spec); + if (!controller) + return -ENOMEM; + } } - - if (path) - *path = NULL; - - return 0; } - t = strndup(spec, e-spec); - if (!t) - return -ENOMEM; - if (!cg_controller_is_valid(t)) { - free(t); - return -EINVAL; - } - - if (isempty(e+1)) - u = NULL; - else { - u = strdup(e+1); - if (!u) { - free(t); - return -ENOMEM; - } - - if (!path_is_normalized(u) || - !path_is_absolute(u)) { - free(t); - free(u); - return -EINVAL; - } - - path_simplify(u, false); - } - - if (controller) - *controller = t; - else - free(t); - - if (path) - *path = u; - else - free(u); - + if (ret_controller) + *ret_controller = TAKE_PTR(controller); + if (ret_path) + *ret_path = TAKE_PTR(path); return 0; } @@ -1663,12 +1669,39 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri return read_one_line_file(p, ret); } -int cg_get_keyed_attribute( +int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret) { + _cleanup_free_ char *value = NULL; + uint64_t v; + int r; + + assert(ret); + + r = cg_get_attribute(controller, path, attribute, &value); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + if (streq(value, "max")) { + *ret = CGROUP_LIMIT_MAX; + return 0; + } + + r = safe_atou64(value, &v); + if (r < 0) + return r; + + *ret = v; + return 0; +} + +int cg_get_keyed_attribute_full( const char *controller, const char *path, const char *attribute, char **keys, - char **ret_values) { + char **ret_values, + CGroupKeyMode mode) { _cleanup_free_ char *filename = NULL, *contents = NULL; const char *p; @@ -1680,7 +1713,8 @@ int cg_get_keyed_attribute( * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of * entries as 'keys'. On success each entry will be set to the value of the matching key. * - * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */ + * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode + * is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */ r = cg_get_path(controller, path, attribute, &filename); if (r < 0) @@ -1728,6 +1762,9 @@ int cg_get_keyed_attribute( p += strspn(p, NEWLINE); } + if (mode & CG_KEY_MODE_GRACEFUL) + goto done; + r = -ENXIO; fail: @@ -1738,6 +1775,9 @@ fail: done: memcpy(ret_values, v, sizeof(char*) * n); + if (mode & CG_KEY_MODE_GRACEFUL) + return n_done; + return 0; } diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index ad1661906..2b88571bc 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -170,7 +170,7 @@ typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); -int cg_split_spec(const char *spec, char **controller, char **path); +int cg_split_spec(const char *spec, char **ret_controller, char **ret_path); int cg_mangle_path(const char *path, char **result); int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); @@ -180,14 +180,39 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +typedef enum { + CG_KEY_MODE_GRACEFUL = 1 << 0, +} CGroupKeyMode; + int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); -int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values); +int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode); + +static inline int cg_get_keyed_attribute( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0); +} + +static inline int cg_get_keyed_attribute_graceful( + const char *controller, + const char *path, + const char *attribute, + char **keys, + char **ret_values) { + return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL); +} + +int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret); int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size); +int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret); int cg_remove_xattr(const char *controller, const char *path, const char *name); int cg_install_release_agent(const char *controller, const char *agent); @@ -235,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret); int cg_kernel_controllers(Set **controllers); bool cg_ns_supported(void); +bool cg_freezer_supported(void); int cg_all_unified(void); int cg_hybrid_unified(void); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 58eb62fb7..eb19516c2 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -77,8 +77,10 @@ static int files_add( /* Is this a masking entry? */ if ((flags & CONF_FILES_FILTER_MASKED)) if (null_or_empty(&st)) { + assert(masked); + /* Mark this one as masked */ - r = set_put_strdup(masked, de->d_name); + r = set_put_strdup(&masked, de->d_name); if (r < 0) return r; diff --git a/src/basic/copy.c b/src/basic/copy.c index 97d566c5b..b384010ae 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -15,6 +15,7 @@ #include "copy.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "io-util.h" #include "macro.h" @@ -569,10 +570,9 @@ static int fd_copy_directory( if (fdf < 0) return -errno; - d = fdopendir(fdf); + d = take_fdopendir(&fdf); if (!d) return -errno; - fdf = -1; exists = false; if (copy_flags & COPY_MERGE_EMPTY) { diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c index 5ebe5b248..888ef2366 100644 --- a/src/basic/device-nodes.c +++ b/src/basic/device-nodes.c @@ -7,7 +7,7 @@ #include "device-nodes.h" #include "utf8.h" -int whitelisted_char_for_devnode(char c, const char *white) { +int allow_listed_char_for_devnode(char c, const char *white) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || @@ -38,7 +38,7 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) { j += seqlen; i += (seqlen-1); - } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) { + } else if (str[i] == '\\' || !allow_listed_char_for_devnode(str[i], NULL)) { if (len-j < 4) return -EINVAL; diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h index 3840e6d30..0dad8c9c6 100644 --- a/src/basic/device-nodes.h +++ b/src/basic/device-nodes.h @@ -8,7 +8,7 @@ #include "stdio-util.h" int encode_devnode_name(const char *str, char *str_enc, size_t len); -int whitelisted_char_for_devnode(char c, const char *additional); +int allow_listed_char_for_devnode(char c, const char *additional); #define DEV_NUM_PATH_MAX \ (STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t)) diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h new file mode 100644 index 000000000..d254afb68 --- /dev/null +++ b/src/basic/dlfcn-util.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "macro.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(void*, dlclose); diff --git a/src/basic/efivars.c b/src/basic/efivars.c index e7edd17d0..007137cf0 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -210,9 +210,9 @@ int efi_set_variable( if (!p) return -ENOMEM; - /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect - * them for accidental removal and modification. We are not changing these variables accidentally however, - * hence let's unset the bit first. */ + /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default, + * to protect them for accidental removal and modification. We are not changing these variables + * accidentally however, hence let's unset the bit first. */ r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags); if (r < 0 && r != -ENOENT) diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h index 65a6384ee..0ca650f48 100644 --- a/src/basic/errno-util.h +++ b/src/basic/errno-util.h @@ -87,12 +87,16 @@ static inline bool ERRNO_IS_RESOURCE(int r) { ENOMEM); } -/* Three different errors for "operation/system call/ioctl not supported" */ +/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */ static inline bool ERRNO_IS_NOT_SUPPORTED(int r) { return IN_SET(abs(r), EOPNOTSUPP, ENOTTY, - ENOSYS); + ENOSYS, + EAFNOSUPPORT, + EPFNOSUPPORT, + EPROTONOSUPPORT, + ESOCKTNOSUPPORT); } /* Two different errors for access problems */ diff --git a/src/basic/escape.c b/src/basic/escape.c index c5c44d2e7..116efa411 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -518,22 +518,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) { return NULL; t = r; - if (style == ESCAPE_BACKSLASH) + switch (style) { + case ESCAPE_BACKSLASH: + case ESCAPE_BACKSLASH_ONELINE: *(t++) = '"'; - else if (style == ESCAPE_POSIX) { + break; + case ESCAPE_POSIX: *(t++) = '$'; *(t++) = '\''; - } else + break; + default: assert_not_reached("Bad EscapeStyle"); + } t = mempcpy(t, s, p - s); - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, + style == ESCAPE_BACKSLASH_ONELINE); else t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - if (style == ESCAPE_BACKSLASH) + if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) *(t++) = '"'; else *(t++) = '\''; diff --git a/src/basic/escape.h b/src/basic/escape.h index b8eb137c3..0b00b116e 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -34,8 +34,13 @@ typedef enum UnescapeFlags { } UnescapeFlags; typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, + ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single + argument, possibly multiline. Tabs and newlines are not escaped. */ + ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line + string instead. Shell escape sequences are produced for tabs and + newlines. */ + ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape + * syntax (a string enclosed in $'') instead of plain quotes. */ } EscapeStyle; char *cescape(const char *s); diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index de3f238d3..75a6282ed 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -21,9 +21,10 @@ #include "path-util.h" #include "process-util.h" #include "socket-util.h" +#include "stat-util.h" #include "stdio-util.h" -#include "util.h" #include "tmpfile-util.h" +#include "util.h" /* The maximum number of iterations in the loop to close descriptors in the fallback case * when /proc/self/fd/ is inaccessible. */ @@ -147,11 +148,7 @@ int fd_nonblock(int fd, bool nonblock) { if (flags < 0) return -errno; - if (nonblock) - nflags = flags | O_NONBLOCK; - else - nflags = flags & ~O_NONBLOCK; - + nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock); if (nflags == flags) return 0; @@ -170,11 +167,7 @@ int fd_cloexec(int fd, bool cloexec) { if (flags < 0) return -errno; - if (cloexec) - nflags = flags | FD_CLOEXEC; - else - nflags = flags & ~FD_CLOEXEC; - + nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec); if (nflags == flags) return 0; @@ -950,8 +943,15 @@ int fd_reopen(int fd, int flags) { xsprintf(procfs_path, "/proc/self/fd/%i", fd); new_fd = open(procfs_path, flags); - if (new_fd < 0) - return -errno; + if (new_fd < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } return new_fd; } diff --git a/src/basic/fileio.c b/src/basic/fileio.c index de5bd78b5..c3d55d209 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -22,6 +22,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "socket-util.h" #include "stdio-util.h" #include "string-util.h" #include "tmpfile-util.h" @@ -54,6 +55,44 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) { return 0; } +int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) { + int r; + + assert(fd); + + r = fdopen_unlocked(*fd, options, ret); + if (r < 0) + return r; + + *fd = -1; + + return 0; +} + +FILE* take_fdopen(int *fd, const char *options) { + assert(fd); + + FILE *f = fdopen(*fd, options); + if (!f) + return NULL; + + *fd = -1; + + return f; +} + +DIR* take_fdopendir(int *dfd) { + assert(dfd); + + DIR *d = fdopendir(*dfd); + if (!d) + return NULL; + + *dfd = -1; + + return d; +} + FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) { FILE *f = open_memstream(ptr, sizeloc); if (!f) @@ -164,6 +203,13 @@ static int write_string_file_atomic( goto fail; } + if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) { + /* Sync the rename, too */ + r = fsync_directory_of_file(fileno(f)); + if (r < 0) + return r; + } + return 0; fail: @@ -437,13 +483,12 @@ int read_full_stream_full( assert(f); assert(ret_contents); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); - assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size); n_next = LINE_MAX; /* Start size */ fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's - * optimize our buffering) */ + if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's + * optimize our buffering */ if (fstat(fd, &st) < 0) return -errno; @@ -460,7 +505,7 @@ int read_full_stream_full( if (st.st_size > 0) n_next = st.st_size + 1; - if (flags & READ_FULL_FILE_SECURE) + if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } @@ -490,21 +535,18 @@ int read_full_stream_full( errno = 0; k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; + + assert(k <= n - l); + l += k; if (ferror(f)) { r = errno_or_else(EIO); goto finalize; } - if (feof(f)) break; - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ /* Safety check */ if (n >= READ_FULL_BYTES_MAX) { @@ -516,12 +558,21 @@ int read_full_stream_full( } if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) { + _cleanup_free_ void *decoded = NULL; + size_t decoded_size; + buf[l++] = 0; if (flags & READ_FULL_FILE_UNBASE64) - r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); else - r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); - goto finalize; + r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size); + if (r < 0) + goto finalize; + + if (flags & READ_FULL_FILE_SECURE) + explicit_bzero_safe(buf, n); + free_and_replace(buf, decoded); + n = l = decoded_size; } if (!ret_size) { @@ -558,8 +609,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag assert(contents); r = xfopenat(dir_fd, filename, "re", 0, &f); - if (r < 0) - return r; + if (r < 0) { + _cleanup_close_ int dfd = -1, sk = -1; + union sockaddr_union sa; + + /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ + if (r != -ENXIO) + return r; + + /* If this is enabled, let's try to connect to it */ + if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) + return -ENXIO; + + if (dir_fd == AT_FDCWD) + r = sockaddr_un_set_path(&sa.un, filename); + else { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + + /* If we shall operate relative to some directory, then let's use O_PATH first to + * open the socket inode, and then connect to it via /proc/self/fd/. We have to do + * this since there's not connectat() that takes a directory fd as first arg. */ + + dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); + if (dfd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", dfd); + r = sockaddr_un_set_path(&sa.un, procfs_path); + } + if (r < 0) + return r; + + sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (sk < 0) + return -errno; + + if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is + * not a socket after all */ + + if (shutdown(sk, SHUT_WR) < 0) + return -errno; + + f = fdopen(sk, "r"); + if (!f) + return -errno; + + TAKE_FD(sk); + } (void) __fsetlocking(f, FSETLOCKING_BYCALLER); @@ -1142,3 +1239,25 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c filename, st->st_mode & 07777); return 0; } + +int sync_rights(int from, int to) { + struct stat st; + + if (fstat(from, &st) < 0) + return -errno; + + return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); +} + +int rename_and_apply_smack_floor_label(const char *from, const char *to) { + int r = 0; + if (rename(from, to) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index d140bfd99..7d58fa7cf 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -32,13 +32,18 @@ typedef enum { } WriteStringFileFlags; typedef enum { - READ_FULL_FILE_SECURE = 1 << 0, - READ_FULL_FILE_UNBASE64 = 1 << 1, - READ_FULL_FILE_UNHEX = 1 << 2, + READ_FULL_FILE_SECURE = 1 << 0, /* erase any buffers we employ internally, after use */ + READ_FULL_FILE_UNBASE64 = 1 << 1, /* base64 decode what we read */ + READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */ + READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */ + READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */ } ReadFullFileFlags; int fopen_unlocked(const char *path, const char *options, FILE **ret); int fdopen_unlocked(int fd, const char *options, FILE **ret); +int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); +FILE* take_fdopen(int *fd, const char *options); +DIR* take_fdopendir(int *dfd); FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc); FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode); @@ -103,3 +108,7 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); + +int sync_rights(int from, int to); + +int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index ef3b5a518..34a226078 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -8,8 +8,10 @@ #include #include "alloc-util.h" +#include "blockdev-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "locale-util.h" #include "log.h" @@ -21,6 +23,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "random-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -337,8 +340,35 @@ int fchmod_opath(int fd, mode_t m) { * fchownat() does. */ xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) - return -errno; + if (chmod(procfs_path, m) < 0) { + if (errno != ENOENT) + return -errno; + + if (proc_mounted() == 0) + return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ + + return -ENOENT; + } + + return 0; +} + +int stat_warn_permissions(const char *path, const struct stat *st) { + assert(path); + assert(st); + + /* Don't complain if we are reading something that is not a file, for example /dev/null */ + if (!S_ISREG(st->st_mode)) + return 0; + + if (st->st_mode & 0111) + log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); + + if (st->st_mode & 0002) + log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); + + if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044) + log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); return 0; } @@ -346,23 +376,13 @@ int fchmod_opath(int fd, mode_t m) { int fd_warn_permissions(const char *path, int fd) { struct stat st; + assert(path); + assert(fd >= 0); + if (fstat(fd, &st) < 0) return -errno; - /* Don't complain if we are reading something that is not a file, for example /dev/null */ - if (!S_ISREG(st.st_mode)) - return 0; - - if (st.st_mode & 0111) - log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - - if (st.st_mode & 0002) - log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - - if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) - log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); - - return 0; + return stat_warn_permissions(path, &st); } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { @@ -1294,11 +1314,13 @@ void unlink_tempfilep(char (*p)[]) { (void) unlink_noerrno(*p); } -int unlinkat_deallocate(int fd, const char *name, int flags) { +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { _cleanup_close_ int truncate_fd = -1; struct stat st; off_t l, bs; + assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); + /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other * link to it. This is useful to ensure that other processes that might have the file open for reading won't be * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up @@ -1315,7 +1337,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the * primary job – to delete the file – is accomplished. */ - if ((flags & AT_REMOVEDIR) == 0) { + if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) { truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); if (truncate_fd < 0) { @@ -1331,7 +1353,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { } } - if (unlinkat(fd, name, flags) < 0) + if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0) return -errno; if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ @@ -1342,7 +1364,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { return 0; } - if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) + if (!S_ISREG(st.st_mode)) + return 0; + + if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) { + uint64_t left = st.st_size; + char buffer[64 * 1024]; + + /* If erasing is requested, let's overwrite the file with random data once before deleting + * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough + * for stuff backed by tmpfs at least. + * + * Note that we only erase like this if the link count of the file is zero. If it is higher it + * is still linked by someone else and we'll leave it to them to remove it securely + * eventually! */ + + random_bytes(buffer, sizeof(buffer)); + + while (left > 0) { + ssize_t n; + + n = write(truncate_fd, buffer, MIN(sizeof(buffer), left)); + if (n < 0) { + log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name); + break; + } + + assert(left >= (size_t) n); + left -= n; + } + + /* Let's refresh metadata */ + if (fstat(truncate_fd, &st) < 0) { + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); + return 0; + } + } + + /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */ + if (st.st_blocks == 0 || st.st_nlink > 0) return 0; /* If this is a regular file, it actually took up space on disk and there are no other links it's time to @@ -1481,3 +1541,88 @@ int open_parent(const char *path, int flags, mode_t mode) { return fd; } + +static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { + _cleanup_free_ char *p = NULL, *uuids = NULL; + _cleanup_closedir_ DIR *d = NULL; + int r, found_encrypted = false; + + assert(sysfs_path); + + if (depth_left == 0) + return -EINVAL; + + p = path_join(sysfs_path, "dm/uuid"); + if (!p) + return -ENOMEM; + + r = read_one_line_file(p, &uuids); + if (r != -ENOENT) { + if (r < 0) + return r; + + /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ + if (startswith(uuids, "CRYPT-")) + return true; + } + + /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" + * subdir. */ + + p = mfree(p); + p = path_join(sysfs_path, "slaves"); + if (!p) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) /* Doesn't have underlying devices */ + return false; + + return -errno; + } + + for (;;) { + _cleanup_free_ char *q = NULL; + struct dirent *de; + + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + return -errno; + + break; /* No more underlying devices */ + } + + q = path_join(p, de->d_name); + if (!q) + return -ENOMEM; + + r = blockdev_is_encrypted(q, depth_left - 1); + if (r < 0) + return r; + if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ + return false; + + found_encrypted = true; + } + + return found_encrypted; +} + +int path_is_encrypted(const char *path) { + char p[SYS_BLOCK_PATH_MAX(NULL)]; + dev_t devt; + int r; + + r = get_block_device(path, &devt); + if (r < 0) + return r; + if (r == 0) /* doesn't have a block device */ + return false; + + xsprintf_sys_block_path(p, NULL, devt); + + return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 6b9ade2ec..b184570f9 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); int fd_warn_permissions(const char *path, int fd); +int stat_warn_permissions(const char *path, const struct stat *st); #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) @@ -82,7 +83,7 @@ enum { CHASE_SAFE = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's + CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's * right-most component refers to symlink, return O_PATH fd of the symlink. */ CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ }; @@ -113,7 +114,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); int access_fd(int fd, int mode); void unlink_tempfilep(char (*p)[]); -int unlinkat_deallocate(int fd, const char *name, int flags); + +typedef enum UnlinkDeallocateFlags { + UNLINK_REMOVEDIR = 1 << 0, + UNLINK_ERASE = 1 << 1, +} UnlinkDeallocateFlags; + +int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); int fsync_directory_of_file(int fd); int fsync_full(int fd); @@ -122,3 +129,5 @@ int fsync_path_at(int at_fd, const char *path); int syncfs_path(int atfd, const char *path); int open_parent(const char *path, int flags, mode_t mode); + +int path_is_encrypted(const char *path); diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index fce339512..cf279e5cb 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -10,6 +10,8 @@ void string_hash_func(const char *p, struct siphash *state) { } DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, + char, string_hash_func, string_compare_func, free); DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, char, free); @@ -53,6 +55,8 @@ void path_hash_func(const char *q, struct siphash *state) { } DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, + char, path_hash_func, path_compare, free); void trivial_hash_func(const void *p, struct siphash *state) { siphash24_compress(&p, sizeof(p), state); diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h index 7bb5d1cd0..005d1b21d 100644 --- a/src/basic/hash-funcs.h +++ b/src/basic/hash-funcs.h @@ -76,10 +76,12 @@ struct hash_ops { void string_hash_func(const char *p, struct siphash *state); #define string_compare_func strcmp extern const struct hash_ops string_hash_ops; +extern const struct hash_ops string_hash_ops_free; extern const struct hash_ops string_hash_ops_free_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; +extern const struct hash_ops path_hash_ops_free; /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings * or suchlike. */ diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 4853514c9..67c439123 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -145,12 +145,7 @@ struct hashmap_debug_info { /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ +#endif enum HashmapType { HASHMAP_TYPE_PLAIN, @@ -212,7 +207,10 @@ struct HashmapBase { bool from_pool:1; /* whether was allocated from mempool */ bool dirty:1; /* whether dirtied since last iterated_cache_get() */ bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ + +#if ENABLE_DEBUG_HASHMAP + struct hashmap_debug_info debug; +#endif }; /* Specific hash types @@ -254,7 +252,7 @@ struct hashmap_type_info { unsigned n_direct_buckets; }; -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { +static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { [HASHMAP_TYPE_PLAIN] = { .head_size = sizeof(Hashmap), .entry_size = sizeof(struct plain_hashmap_entry), @@ -707,7 +705,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { : hashmap_iterate_in_internal_order(h, i); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { struct hashmap_base_entry *e; void *data; unsigned idx; @@ -733,7 +731,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v } bool set_iterate(const Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); + return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL); } #define HASHMAP_FOREACH_IDX(idx, h, i) \ @@ -741,7 +739,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) { (idx != IDX_NIL); \ (idx) = hashmap_iterate_entry((h), &(i))) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) { IteratedCache *cache; assert(h); @@ -770,7 +768,7 @@ static void reset_direct_storage(HashmapBase *h) { memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); } -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { +static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; bool up; @@ -809,20 +807,20 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu return h; } -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { + enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *q; assert(h); @@ -830,24 +828,24 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops if (*h) return 0; - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); + q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); if (!q) return -ENOMEM; *h = q; - return 0; + return 1; } -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); } -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } static void hashmap_free_no_clear(HashmapBase *h) { @@ -868,16 +866,16 @@ static void hashmap_free_no_clear(HashmapBase *h) { free(h); } -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { if (h) { - internal_hashmap_clear(h, default_free_key, default_free_value); + _hashmap_clear(h, default_free_key, default_free_value); hashmap_free_no_clear(h); } return NULL; } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { free_func_t free_key, free_value; if (!h) return; @@ -891,11 +889,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f * hash table, and only then call the destructor functions. If these destructors then try to unregister * themselves from our hash table a second time, the entry is already gone. */ - while (internal_hashmap_size(h) > 0) { + while (_hashmap_size(h) > 0) { void *k = NULL; void *v; - v = internal_hashmap_first_key_and_value(h, true, &k); + v = _hashmap_first_key_and_value(h, true, &k); if (free_key) free_key(k); @@ -1249,6 +1247,30 @@ int set_put(Set *s, const void *key) { return hashmap_put_boldly(s, hash, &swap, true); } +int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS) { + int r; + + r = _set_ensure_allocated(s, hash_ops HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + return set_put(*s, key); +} + +int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS) { + int r; + + r = _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_PASS_ARGS); + if (r <= 0) { + if (hash_ops && hash_ops->free_key) + hash_ops->free_key(key); + else + free(key); + } + + return r; +} + int hashmap_replace(Hashmap *h, const void *key, void *value) { struct swap_entries swap; struct plain_hashmap_entry *e; @@ -1301,7 +1323,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) { return 0; } -void *internal_hashmap_get(HashmapBase *h, const void *key) { +void *_hashmap_get(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1336,7 +1358,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) { return e->value; } -bool internal_hashmap_contains(HashmapBase *h, const void *key) { +bool _hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) @@ -1346,7 +1368,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) { return bucket_scan(h, hash, key) != IDX_NIL; } -void *internal_hashmap_remove(HashmapBase *h, const void *key) { +void *_hashmap_remove(HashmapBase *h, const void *key) { struct hashmap_base_entry *e; unsigned hash, idx; void *data; @@ -1484,7 +1506,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_ return 0; } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) { struct hashmap_base_entry *e; unsigned hash, idx; @@ -1514,7 +1536,7 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; void *key, *data; unsigned idx; @@ -1539,21 +1561,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r return data; } -unsigned internal_hashmap_size(HashmapBase *h) { +unsigned _hashmap_size(HashmapBase *h) { if (!h) return 0; return n_entries(h); } -unsigned internal_hashmap_buckets(HashmapBase *h) { +unsigned _hashmap_buckets(HashmapBase *h) { if (!h) return 0; return n_buckets(h); } -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { +int _hashmap_merge(Hashmap *h, Hashmap *other) { Iterator i; unsigned idx; @@ -1589,7 +1611,7 @@ int set_merge(Set *s, Set *other) { return 0; } -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { +int _hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); @@ -1607,7 +1629,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { * Returns: 0 on success. * -ENOMEM on alloc failure, in which case no move has been done. */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { +int _hashmap_move(HashmapBase *h, HashmapBase *other) { struct swap_entries swap; struct hashmap_base_entry *e, *n; Iterator i; @@ -1652,7 +1674,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { return 0; } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { struct swap_entries swap; unsigned h_hash, other_hash, idx; struct hashmap_base_entry *e, *n; @@ -1689,13 +1711,13 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke return 0; } -HashmapBase *internal_hashmap_copy(HashmapBase *h) { +HashmapBase *_hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) { HashmapBase *copy; int r; assert(h); - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); + copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_PASS_ARGS); if (!copy) return NULL; @@ -1711,15 +1733,13 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) { assert_not_reached("Unknown hashmap type"); } - if (r < 0) { - internal_hashmap_free(copy, false, false); - return NULL; - } + if (r < 0) + return _hashmap_free(copy, false, false); return copy; } -char **internal_hashmap_get_strv(HashmapBase *h) { +char **_hashmap_get_strv(HashmapBase *h) { char **sv; Iterator i; unsigned idx, n; @@ -1767,56 +1787,69 @@ int set_consume(Set *s, void *value) { return r; } -int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { +int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_hash_ops_free_free); + r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; _cleanup_free_ char *kdup = NULL, *vdup = NULL; + kdup = strdup(k); - vdup = strdup(v); - if (!kdup || !vdup) + if (!kdup) return -ENOMEM; + if (v) { + vdup = strdup(v); + if (!vdup) + return -ENOMEM; + } + r = hashmap_put(*h, kdup, vdup); if (r < 0) { - if (r == -EEXIST && streq(v, hashmap_get(*h, kdup))) + if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup))) return 0; return r; } - assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */ - kdup = vdup = NULL; + /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */ + assert(vdup == NULL || r > 0); + if (r > 0) + kdup = vdup = NULL; - return 0; + return r; } -int set_put_strdup(Set *s, const char *p) { +int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS) { char *c; + int r; assert(s); assert(p); - if (set_contains(s, (char*) p)) + r = _set_ensure_allocated(s, &string_hash_ops_free HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + if (set_contains(*s, (char*) p)) return 0; c = strdup(p); if (!c) return -ENOMEM; - return set_consume(s, c); + return set_consume(*s, c); } -int set_put_strdupv(Set *s, char **l) { +int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; char **i; assert(s); STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); + r = _set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index 65adc9251..600944162 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -14,7 +14,7 @@ * will be treated as empty hashmap for all read operations. That way it is not * necessary to instantiate an object for each Hashmap use. * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), + * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap), * the implementation will: * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) * - perform extra checks for invalid use of iterators @@ -24,10 +24,9 @@ typedef void* (*hashmap_destroy_t)(void *p); -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ +/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*) + * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call + * underscore-prefixed functions directly). */ typedef struct HashmapBase HashmapBase; /* Specific hashmap/set types */ @@ -84,62 +83,66 @@ typedef struct { # define HASHMAP_DEBUG_PASS_ARGS #endif -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +Hashmap *_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +#define hashmap_free_and_replace(a, b) \ + ({ \ + hashmap_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) + +HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); } static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free); } static inline Hashmap *hashmap_free_free_key(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL); } static inline Hashmap *hashmap_free_free_free(Hashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free); + return (void*) _hashmap_free(HASHMAP_BASE(h), free, free); } IteratedCache *iterated_cache_free(IteratedCache *cache); int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); -HashmapBase *internal_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} +HashmapBase *_hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS); +#define hashmap_copy(h) ((Hashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) +#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); +IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); + return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); } int hashmap_put(Hashmap *h, const void *key, void *value); @@ -147,7 +150,8 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void * return hashmap_put(PLAIN_HASHMAP(h), key, value); } -int hashmap_put_strdup(Hashmap **h, const char *k, const char *v); +int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v HASHMAP_DEBUG_PARAMS); +#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v HASHMAP_DEBUG_SRC_ARGS) int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { @@ -159,12 +163,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo return hashmap_replace(PLAIN_HASHMAP(h), key, value); } -void *internal_hashmap_get(HashmapBase *h, const void *key); +void *_hashmap_get(HashmapBase *h, const void *key); static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); + return _hashmap_get(HASHMAP_BASE(h), key); } void *hashmap_get2(Hashmap *h, const void *key, void **rkey); @@ -172,20 +176,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); } -bool internal_hashmap_contains(HashmapBase *h, const void *key); +bool _hashmap_contains(HashmapBase *h, const void *key); static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); + return _hashmap_contains(HASHMAP_BASE(h), key); } -void *internal_hashmap_remove(HashmapBase *h, const void *key); +void *_hashmap_remove(HashmapBase *h, const void *key); static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); + return _hashmap_remove(HASHMAP_BASE(h), key); } void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); @@ -193,9 +197,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } -void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value); +void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value); static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value); + return _hashmap_remove_value(HASHMAP_BASE(h), key, value); } static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { @@ -214,41 +218,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +int _hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) #define ordered_hashmap_merge(h, other) hashmap_merge(h, other) -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +int _hashmap_reserve(HashmapBase *h, unsigned entries_add); static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +int _hashmap_move(HashmapBase *h, HashmapBase *other); /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned internal_hashmap_size(HashmapBase *h) _pure_; +unsigned _hashmap_size(HashmapBase *h) _pure_; static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); + return _hashmap_size(HASHMAP_BASE(h)); } static inline bool hashmap_isempty(Hashmap *h) { @@ -258,49 +262,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { return ordered_hashmap_size(h) == 0; } -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +unsigned _hashmap_buckets(HashmapBase *h) _pure_; static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); + return _hashmap_buckets(HASHMAP_BASE(h)); } -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); +bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); + return _hashmap_iterate(HASHMAP_BASE(h), i, value, key); } -void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); +void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value); static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(h), NULL, NULL); } static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), NULL, free); + _hashmap_clear(HASHMAP_BASE(h), NULL, free); } static inline void hashmap_clear_free_key(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, NULL); + _hashmap_clear(HASHMAP_BASE(h), free, NULL); } static inline void hashmap_clear_free_free(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h), free, free); + _hashmap_clear(HASHMAP_BASE(h), free, free); } /* @@ -314,50 +318,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); } static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); } static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { +static inline void *_hashmap_first_key(HashmapBase *h, bool remove) { void *key = NULL; - (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); return key; } static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), true); + return _hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h), false); + return _hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ @@ -386,12 +390,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { /* no hashmap_next */ void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **internal_hashmap_get_strv(HashmapBase *h); +char **_hashmap_get_strv(HashmapBase *h); static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); + return _hashmap_get_strv(HASHMAP_BASE(h)); } /* diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 7bc2e3f37..90a3dfc86 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -12,6 +12,7 @@ #include "hostname-util.h" #include "macro.h" #include "string-util.h" +#include "strv.h" bool hostname_is_set(void) { struct utsname u; @@ -21,7 +22,7 @@ bool hostname_is_set(void) { if (isempty(u.nodename)) return false; - /* This is the built-in kernel default host name */ + /* This is the built-in kernel default hostname */ if (streq(u.nodename, "(none)")) return false; @@ -30,6 +31,7 @@ bool hostname_is_set(void) { char* gethostname_malloc(void) { struct utsname u; + const char *s; /* This call tries to return something useful, either the actual hostname * or it makes something up. The only reason it might fail is OOM. @@ -37,10 +39,28 @@ char* gethostname_malloc(void) { assert_se(uname(&u) >= 0); - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(FALLBACK_HOSTNAME); + s = u.nodename; + if (isempty(s) || streq(s, "(none)")) + s = FALLBACK_HOSTNAME; - return strdup(u.nodename); + return strdup(s); +} + +char* gethostname_short_malloc(void) { + struct utsname u; + const char *s; + + /* Like above, but kills the FQDN part if present. */ + + assert_se(uname(&u) >= 0); + + s = u.nodename; + if (isempty(s) || streq(s, "(none)") || s[0] == '.') { + s = FALLBACK_HOSTNAME; + assert(s[0] != '.'); + } + + return strndup(s, strcspn(s, ".")); } int gethostname_strict(char **ret) { @@ -77,7 +97,7 @@ bool valid_ldh_char(char c) { } /** - * Check if s looks like a valid host name or FQDN. This does not do + * Check if s looks like a valid hostname or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if @@ -180,14 +200,16 @@ bool is_localhost(const char *hostname) { /* This tries to identify local host and domain names * described in RFC6761 plus the redhatism of localdomain */ - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); + return STRCASE_IN_SET( + hostname, + "localhost", + "localhost.", + "localhost.localdomain", + "localhost.localdomain.") || + endswith_no_case(hostname, ".localhost") || + endswith_no_case(hostname, ".localhost.") || + endswith_no_case(hostname, ".localhost.localdomain") || + endswith_no_case(hostname, ".localhost.localdomain."); } bool is_gateway_hostname(const char *hostname) { diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 7ba386a0f..cafd6f020 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -9,6 +9,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); +char* gethostname_short_malloc(void); int gethostname_strict(char **ret); bool valid_ldh_char(char c) _const_; diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index bfe855fb4..ea50e2619 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -14,6 +14,7 @@ #include "macro.h" #include "parse-util.h" #include "random-util.h" +#include "string-util.h" #include "strxcpyx.h" #include "util.h" @@ -107,11 +108,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return in4_addr_equal(&a->in, &b->in); if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; + return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6); return -EAFNOSUPPORT; } @@ -177,47 +174,89 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) assert(u); /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ + * positive if that succeeds, or -ERANGE if this overflows. */ + + return in_addr_prefix_nth(family, u, prefixlen, 1); +} + +/* + * Calculates the nth prefix of size prefixlen starting from the address denoted by u. + * + * On success 1 will be returned and the calculated prefix will be available in + * u. In the case nth == 0 the input will be left unchanged and 1 will be returned. + * In case the calculation cannot be performed (invalid prefix length, + * overflows would occur) -ERANGE is returned. If the address family given isn't + * supported -EAFNOSUPPORT will be returned. + * + * + * Examples: + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written + * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u + */ +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) { + assert(u); if (prefixlen <= 0) - return 0; + return -ERANGE; + + if (nth == 0) + return 1; if (family == AF_INET) { - uint32_t c, n; - + uint32_t c, n, t; if (prefixlen > 32) prefixlen = 32; c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); + t = nth << (32 - prefixlen); + + /* Check for wrap */ + if (c > UINT32_MAX - t) + return -ERANGE; + + n = c + t; + + n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen); u->in.s_addr = htobe32(n); return 1; } if (family == AF_INET6) { - struct in6_addr add = {}, result; + struct in6_addr result = {}; uint8_t overflow = 0; - unsigned i; + uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */ + unsigned start_byte = (prefixlen - 1) / 8; if (prefixlen > 128) prefixlen = 128; /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); + delta = nth << ((128 - prefixlen) % 8); - for (i = 16; i > 0; i--) { + for (unsigned i = 16; i > 0; i--) { unsigned j = i - 1; + unsigned d = 0; - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); + if (j <= start_byte) { + int16_t t; + + d = delta & 0xFF; + delta >>= 8; + + t = u->in6.s6_addr[j] + d + overflow; + overflow = t > UINT8_MAX ? t - UINT8_MAX : 0; + + result.s6_addr[j] = (uint8_t)t; + } else + result.s6_addr[j] = u->in6.s6_addr[j]; } - if (overflow) - return 0; + if (overflow || delta != 0) + return -ERANGE; u->in6 = result; return 1; @@ -403,6 +442,61 @@ fallback: return in_addr_to_string(family, u, ret); } +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) { + _cleanup_free_ char *ip_str = NULL, *x = NULL; + int r; + + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(u); + assert(ret); + + /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly + * handle IPv6 link-local addresses. */ + + r = in_addr_to_string(family, u, &ip_str); + if (r < 0) + return r; + + if (family == AF_INET6) { + r = in_addr_is_link_local(family, u); + if (r < 0) + return r; + if (r == 0) + ifindex = 0; + } else + ifindex = 0; /* For IPv4 address, ifindex is always ignored. */ + + if (port == 0 && ifindex == 0 && isempty(server_name)) { + *ret = TAKE_PTR(ip_str); + return 0; + } + + const char *separator = isempty(server_name) ? "" : "#"; + server_name = strempty(server_name); + + if (port > 0) { + if (family == AF_INET6) { + if (ifindex > 0) + r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name); + else + r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else + r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else { + if (ifindex > 0) + r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name); + else { + x = strjoin(ip_str, separator, server_name); + r = x ? 0 : -ENOMEM; + } + } + if (r < 0) + return -ENOMEM; + + *ret = TAKE_PTR(x); + return 0; +} + int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { union in_addr_union buffer; assert(s); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index ae2dad0bb..dc3f575bc 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -36,10 +36,12 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); +int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); diff --git a/src/basic/io-util.c b/src/basic/io-util.c index c906fc074..18baadb1f 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -11,10 +11,6 @@ #include "time-util.h" int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int count = 0; /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything @@ -27,19 +23,18 @@ int flush_fd(int fd) { ssize_t l; int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; - - } else if (r == 0) + return r; + } + if (r == 0) return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { - if (errno == EINTR) continue; @@ -155,21 +150,15 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { } int pipe_eof(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN|POLLHUP, - }; - int r; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) - return -errno; - + return r; if (r == 0) return 0; - return pollfd.revents & POLLHUP; + return !!(r & POLLHUP); } int fd_wait_for_event(int fd, int event, usec_t t) { @@ -188,6 +177,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) { if (r == 0) return 0; + if (pollfd.revents & POLLNVAL) + return -EBADF; + return pollfd.revents; } diff --git a/src/basic/label.c b/src/basic/label.c index 12a7fb094..741c43c2b 100644 --- a/src/basic/label.c +++ b/src/basic/label.c @@ -10,11 +10,11 @@ #include "selinux-util.h" #include "smack-util.h" -int label_fix(const char *path, LabelFixFlags flags) { +int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) { int r, q; - r = mac_selinux_fix(path, flags); - q = mac_smack_fix(path, flags); + r = mac_selinux_fix_container(path, inside_path, flags); + q = mac_smack_fix_container(path, inside_path, flags); if (r < 0) return r; @@ -45,6 +45,26 @@ int symlink_label(const char *old_path, const char *new_path) { return mac_smack_fix(new_path, 0); } +int mknod_label(const char *pathname, mode_t mode, dev_t dev) { + int r; + + assert(pathname); + + r = mac_selinux_create_file_prepare(pathname, mode); + if (r < 0) + return r; + + if (mknod(pathname, mode, dev) < 0) + r = -errno; + + mac_selinux_create_file_clear(); + + if (r < 0) + return r; + + return mac_smack_fix(pathname, 0); +} + int btrfs_subvol_make_label(const char *path) { int r; diff --git a/src/basic/label.h b/src/basic/label.h index 594fd6597..6dc0f710e 100644 --- a/src/basic/label.h +++ b/src/basic/label.h @@ -9,10 +9,14 @@ typedef enum LabelFixFlags { LABEL_IGNORE_EROFS = 1 << 1, } LabelFixFlags; -int label_fix(const char *path, LabelFixFlags flags); +int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags); +static inline int label_fix(const char *path, LabelFixFlags flags) { + return label_fix_container(path, path, flags); +} int mkdir_label(const char *path, mode_t mode); int mkdirat_label(int dirfd, const char *path, mode_t mode); int symlink_label(const char *old_path, const char *new_path); +int mknod_label(const char *pathname, mode_t mode, dev_t dev); int btrfs_subvol_make_label(const char *path); diff --git a/src/basic/limits-util.c b/src/basic/limits-util.c index 3242e9c7f..9da767a57 100644 --- a/src/basic/limits-util.c +++ b/src/basic/limits-util.c @@ -125,16 +125,9 @@ uint64_t system_tasks_max(void) { if (r < 0) log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); else { - _cleanup_free_ char *value = NULL; - - r = cg_get_attribute("pids", root, "pids.max", &value); + r = cg_get_attribute_as_uint64("pids", root, "pids.max", &b); if (r < 0) log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); - else if (!streq(value, "max")) { - r = safe_atou64(value, &b); - if (r < 0) - log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); - } } return MIN3(TASKS_MAX, diff --git a/src/basic/linux/can/netlink.h b/src/basic/linux/can/netlink.h new file mode 100644 index 000000000..6f598b738 --- /dev/null +++ b/src/basic/linux/can/netlink.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program 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. + */ + +#ifndef _UAPI_CAN_NETLINK_H +#define _UAPI_CAN_NETLINK_H + +#include + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN hardware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segment 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ +#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ + +/* + * CAN device statistics + */ +struct can_device_stats { + __u32 bus_error; /* Bus errors */ + __u32 error_warning; /* Changes to error warning state */ + __u32 error_passive; /* Changes to error passive state */ + __u32 bus_off; /* Changes to bus off state */ + __u32 arbitration_lost; /* Arbitration lost errors */ + __u32 restarts; /* CAN controller re-starts */ +}; + +/* + * CAN netlink interface + */ +enum { + IFLA_CAN_UNSPEC, + IFLA_CAN_BITTIMING, + IFLA_CAN_BITTIMING_CONST, + IFLA_CAN_CLOCK, + IFLA_CAN_STATE, + IFLA_CAN_CTRLMODE, + IFLA_CAN_RESTART_MS, + IFLA_CAN_RESTART, + IFLA_CAN_BERR_COUNTER, + IFLA_CAN_DATA_BITTIMING, + IFLA_CAN_DATA_BITTIMING_CONST, + IFLA_CAN_TERMINATION, + IFLA_CAN_TERMINATION_CONST, + IFLA_CAN_BITRATE_CONST, + IFLA_CAN_DATA_BITRATE_CONST, + IFLA_CAN_BITRATE_MAX, + __IFLA_CAN_MAX +}; + +#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) + +/* u16 termination range: 1..65535 Ohms */ +#define CAN_TERMINATION_DISABLED 0 + +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/src/basic/linux/hdlc/ioctl.h b/src/basic/linux/hdlc/ioctl.h new file mode 100644 index 000000000..b06341aca --- /dev/null +++ b/src/basic/linux/hdlc/ioctl.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + + +#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */ + +#define CLOCK_DEFAULT 0 /* Default setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int dlci; + char master[IFNAMSIZ]; /* Name of master FRAD device */ +}fr_proto_pvc_info; /* for returning PVC information only */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +typedef struct { + unsigned short dce; /* 1 for DCE (network side) operation */ + unsigned int modulo; /* modulo (8 = basic / 128 = extended) */ + unsigned int window; /* frame window size */ + unsigned int t1; /* timeout t1 */ + unsigned int t2; /* timeout t2 */ + unsigned int n2; /* frame retry counter */ +} x25_hdlc_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +#endif /* __ASSEMBLY__ */ +#endif /* __HDLC_IOCTL_H__ */ diff --git a/src/basic/linux/if.h b/src/basic/linux/if.h index a5ae898ff..59948c2f7 100644 --- a/src/basic/linux/if.h +++ b/src/basic/linux/if.h @@ -212,6 +212,7 @@ struct if_settings { fr_proto *fr; fr_proto_pvc *fr_pvc; fr_proto_pvc_info *fr_pvc_info; + x25_hdlc_proto *x25; /* interface settings */ sync_serial_settings *sync; diff --git a/src/basic/linux/if_bonding.h b/src/basic/linux/if_bonding.h index 790585f0e..45f3750aa 100644 --- a/src/basic/linux/if_bonding.h +++ b/src/basic/linux/if_bonding.h @@ -95,6 +95,16 @@ #define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ #define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ +/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */ +#define LACP_STATE_LACP_ACTIVITY 0x1 +#define LACP_STATE_LACP_TIMEOUT 0x2 +#define LACP_STATE_AGGREGATION 0x4 +#define LACP_STATE_SYNCHRONIZATION 0x8 +#define LACP_STATE_COLLECTING 0x10 +#define LACP_STATE_DISTRIBUTING 0x20 +#define LACP_STATE_DEFAULTED 0x40 +#define LACP_STATE_EXPIRED 0x80 + typedef struct ifbond { __s32 bond_mode; __s32 num_slaves; diff --git a/src/basic/linux/if_bridge.h b/src/basic/linux/if_bridge.h index 1b3c2b643..42f7ca38a 100644 --- a/src/basic/linux/if_bridge.h +++ b/src/basic/linux/if_bridge.h @@ -130,6 +130,7 @@ enum { #define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ #define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ #define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ +#define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */ struct bridge_vlan_info { __u16 flags; @@ -156,6 +157,45 @@ struct bridge_vlan_xstats { __u32 pad2; }; +struct bridge_stp_xstats { + __u64 transition_blk; + __u64 transition_fwd; + __u64 rx_bpdu; + __u64 tx_bpdu; + __u64 rx_tcn; + __u64 tx_tcn; +}; + +/* Bridge vlan RTM header */ +struct br_vlan_msg { + __u8 family; + __u8 reserved1; + __u16 reserved2; + __u32 ifindex; +}; + +/* Bridge vlan RTM attributes + * [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_INFO] + * ... + * } + */ +enum { + BRIDGE_VLANDB_UNSPEC, + BRIDGE_VLANDB_ENTRY, + __BRIDGE_VLANDB_MAX, +}; +#define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1) + +enum { + BRIDGE_VLANDB_ENTRY_UNSPEC, + BRIDGE_VLANDB_ENTRY_INFO, + BRIDGE_VLANDB_ENTRY_RANGE, + BRIDGE_VLANDB_ENTRY_STATE, + __BRIDGE_VLANDB_ENTRY_MAX, +}; +#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) + /* Bridge multicast database attributes * [MDBA_MDB] = { * [MDBA_MDB_ENTRY] = { @@ -262,6 +302,7 @@ enum { BRIDGE_XSTATS_VLAN, BRIDGE_XSTATS_MCAST, BRIDGE_XSTATS_PAD, + BRIDGE_XSTATS_STP, __BRIDGE_XSTATS_MAX }; #define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) diff --git a/src/basic/linux/if_link.h b/src/basic/linux/if_link.h index 8aec8769d..024af2d1d 100644 --- a/src/basic/linux/if_link.h +++ b/src/basic/linux/if_link.h @@ -169,6 +169,7 @@ enum { IFLA_MAX_MTU, IFLA_PROP_LIST, IFLA_ALT_IFNAME, /* Alternative ifname */ + IFLA_PERM_ADDRESS, __IFLA_MAX }; @@ -485,6 +486,13 @@ enum macsec_validation_type { MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, }; +enum macsec_offload { + MACSEC_OFFLOAD_OFF = 0, + MACSEC_OFFLOAD_PHY = 1, + __MACSEC_OFFLOAD_END, + MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, +}; + /* IPVLAN section */ enum { IFLA_IPVLAN_UNSPEC, diff --git a/src/basic/linux/if_macsec.h b/src/basic/linux/if_macsec.h index 98e4d5d7c..1d63c43c3 100644 --- a/src/basic/linux/if_macsec.h +++ b/src/basic/linux/if_macsec.h @@ -45,6 +45,7 @@ enum macsec_attrs { MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + MACSEC_ATTR_OFFLOAD, /* config, nested, macsec_offload_attrs */ __MACSEC_ATTR_END, NUM_MACSEC_ATTR = __MACSEC_ATTR_END, MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, @@ -97,6 +98,15 @@ enum macsec_sa_attrs { MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, }; +enum macsec_offload_attrs { + MACSEC_OFFLOAD_ATTR_UNSPEC, + MACSEC_OFFLOAD_ATTR_TYPE, /* config/dump, u8 0..2 */ + MACSEC_OFFLOAD_ATTR_PAD, + __MACSEC_OFFLOAD_ATTR_END, + NUM_MACSEC_OFFLOAD_ATTR = __MACSEC_OFFLOAD_ATTR_END, + MACSEC_OFFLOAD_ATTR_MAX = __MACSEC_OFFLOAD_ATTR_END - 1, +}; + enum macsec_nl_commands { MACSEC_CMD_GET_TXSC, MACSEC_CMD_ADD_RXSC, @@ -108,6 +118,7 @@ enum macsec_nl_commands { MACSEC_CMD_ADD_RXSA, MACSEC_CMD_DEL_RXSA, MACSEC_CMD_UPD_RXSA, + MACSEC_CMD_UPD_OFFLOAD, }; /* u64 per-RXSC stats */ diff --git a/src/basic/linux/in.h b/src/basic/linux/in.h index e7ad9d350..1521073b6 100644 --- a/src/basic/linux/in.h +++ b/src/basic/linux/in.h @@ -76,6 +76,8 @@ enum { #define IPPROTO_MPLS IPPROTO_MPLS IPPROTO_RAW = 255, /* Raw IP packets */ #define IPPROTO_RAW IPPROTO_RAW + IPPROTO_MPTCP = 262, /* Multipath TCP connection */ +#define IPPROTO_MPTCP IPPROTO_MPTCP IPPROTO_MAX }; #endif diff --git a/src/basic/linux/pkt_sched.h b/src/basic/linux/pkt_sched.h index 9f1a72876..bbe791b24 100644 --- a/src/basic/linux/pkt_sched.h +++ b/src/basic/linux/pkt_sched.h @@ -971,6 +971,37 @@ struct tc_pie_xstats { __u32 ecn_mark; /* packets marked with ecn*/ }; +/* FQ PIE */ +enum { + TCA_FQ_PIE_UNSPEC, + TCA_FQ_PIE_LIMIT, + TCA_FQ_PIE_FLOWS, + TCA_FQ_PIE_TARGET, + TCA_FQ_PIE_TUPDATE, + TCA_FQ_PIE_ALPHA, + TCA_FQ_PIE_BETA, + TCA_FQ_PIE_QUANTUM, + TCA_FQ_PIE_MEMORY_LIMIT, + TCA_FQ_PIE_ECN_PROB, + TCA_FQ_PIE_ECN, + TCA_FQ_PIE_BYTEMODE, + TCA_FQ_PIE_DQ_RATE_ESTIMATOR, + __TCA_FQ_PIE_MAX +}; +#define TCA_FQ_PIE_MAX (__TCA_FQ_PIE_MAX - 1) + +struct tc_fq_pie_xstats { + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to fq_pie_action */ + __u32 overlimit; /* dropped due to lack of space in queue */ + __u32 overmemory; /* dropped due to lack of memory in queue */ + __u32 ecn_mark; /* packets marked with ecn */ + __u32 new_flow_count; /* count of new flows created by packets */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 memory_usage; /* total memory across all queues */ +}; + /* CBS */ struct tc_cbs_qopt { __u8 offload; @@ -1187,4 +1218,21 @@ enum { #define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1) +/* ETS */ + +#define TCQ_ETS_MAX_BANDS 16 + +enum { + TCA_ETS_UNSPEC, + TCA_ETS_NBANDS, /* u8 */ + TCA_ETS_NSTRICT, /* u8 */ + TCA_ETS_QUANTA, /* nested TCA_ETS_QUANTA_BAND */ + TCA_ETS_QUANTA_BAND, /* u32 */ + TCA_ETS_PRIOMAP, /* nested TCA_ETS_PRIOMAP_BAND */ + TCA_ETS_PRIOMAP_BAND, /* u8 */ + __TCA_ETS_MAX, +}; + +#define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + #endif diff --git a/src/basic/linux/rtnetlink.h b/src/basic/linux/rtnetlink.h index 1418a8362..4a8c5b745 100644 --- a/src/basic/linux/rtnetlink.h +++ b/src/basic/linux/rtnetlink.h @@ -171,6 +171,13 @@ enum { RTM_GETLINKPROP, #define RTM_GETLINKPROP RTM_GETLINKPROP + RTM_NEWVLAN = 112, +#define RTM_NEWNVLAN RTM_NEWVLAN + RTM_DELVLAN, +#define RTM_DELVLAN RTM_DELVLAN + RTM_GETVLAN, +#define RTM_GETVLAN RTM_GETVLAN + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -309,6 +316,8 @@ enum rt_scope_t { #define RTM_F_PREFIX 0x800 /* Prefix addresses */ #define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ #define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ +#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */ +#define RTM_F_TRAP 0x8000 /* route is trapping packets */ /* Reserved table identifiers */ @@ -721,6 +730,8 @@ enum rtnetlink_groups { #define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R RTNLGRP_NEXTHOP, #define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP + RTNLGRP_BRVLAN, +#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index 96151ffbf..8e6a12b60 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -254,6 +254,21 @@ bool locale_is_valid(const char *name) { return true; } +int locale_is_installed(const char *name) { + if (!locale_is_valid(name)) + return false; + + if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */ + return true; + + _cleanup_(freelocalep) locale_t loc = + newlocale(LC_ALL_MASK, name, 0); + if (loc == (locale_t) 0) + return errno == ENOMEM ? -ENOMEM : false; + + return true; +} + void init_gettext(void) { setlocale(LC_ALL, ""); textdomain(GETTEXT_PACKAGE); @@ -305,7 +320,7 @@ out: return (bool) cached_answer; } -static bool emoji_enabled(void) { +bool emoji_enabled(void) { static int cached_emoji_enabled = -1; if (cached_emoji_enabled < 0) { @@ -350,6 +365,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_SIGMA] = "S", [SPECIAL_GLYPH_ARROW] = "->", [SPECIAL_GLYPH_ELLIPSIS] = "...", + [SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", [SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}", [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)", @@ -357,6 +373,8 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(", [SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{", [SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[", + [SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,", + [SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */ }, /* UTF-8 */ @@ -384,6 +402,9 @@ const char *special_glyph(SpecialGlyph code) { /* Single glyph in Unicode, three in ASCII */ [SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … (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 []) */ + /* 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) */ @@ -392,12 +413,18 @@ const char *special_glyph(SpecialGlyph code) { [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) */ + + /* 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) */ + + /* 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 */ }, }; assert(code < _SPECIAL_GLYPH_MAX); - return draw_table[code >= _SPECIAL_GLYPH_FIRST_SMILEY ? emoji_enabled() : is_locale_utf8()][code]; + return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code]; } void locale_variables_free(char *l[_VARIABLE_LC_MAX]) { diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index cefc4e7f0..aa25e17f1 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -31,6 +31,7 @@ typedef enum LocaleVariable { int get_locales(char ***l); bool locale_is_valid(const char *name); +int locale_is_installed(const char *name); #define _(String) gettext(String) #define N_(String) String @@ -54,19 +55,24 @@ typedef enum { SPECIAL_GLYPH_LIGHT_SHADE, SPECIAL_GLYPH_DARK_SHADE, SPECIAL_GLYPH_SIGMA, - _SPECIAL_GLYPH_FIRST_SMILEY, - SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY, + SPECIAL_GLYPH_EXTERNAL_LINK, + _SPECIAL_GLYPH_FIRST_EMOJI, + SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI, SPECIAL_GLYPH_HAPPY_SMILEY, SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY, SPECIAL_GLYPH_NEUTRAL_SMILEY, SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY, SPECIAL_GLYPH_UNHAPPY_SMILEY, SPECIAL_GLYPH_DEPRESSED_SMILEY, - _SPECIAL_GLYPH_MAX + SPECIAL_GLYPH_LOCK_AND_KEY, + SPECIAL_GLYPH_TOUCH, + _SPECIAL_GLYPH_MAX, } SpecialGlyph; const char *special_glyph(SpecialGlyph code) _const_; +bool emoji_enabled(void); + const char* locale_variable_to_string(LocaleVariable i) _const_; LocaleVariable locale_variable_from_string(const char *s) _pure_; diff --git a/src/basic/log.c b/src/basic/log.c index 17557e184..c6fe20380 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ static bool syslog_is_stream = false; static bool show_color = false; static bool show_location = false; +static bool show_time = false; static bool upgrade_syslog_to_journal = false; static bool always_reopen_console = false; @@ -218,6 +220,32 @@ fail: return r; } +static bool stderr_is_journal(void) { + _cleanup_free_ char *w = NULL; + const char *e; + uint64_t dev, ino; + struct stat st; + + e = getenv("JOURNAL_STREAM"); + if (!e) + return false; + + if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0) + return false; + if (!e) + return false; + + if (safe_atou64(w, &dev) < 0) + return false; + if (safe_atou64(e, &ino) < 0) + return false; + + if (fstat(STDERR_FILENO, &st) < 0) + return false; + + return st.st_dev == dev && st.st_ino == ino; +} + int log_open(void) { int r; @@ -237,9 +265,7 @@ int log_open(void) { return 0; } - if (log_target != LOG_TARGET_AUTO || - getpid_cached() == 1 || - isatty(STDERR_FILENO) <= 0) { + if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) { if (!prohibit_ipc && IN_SET(log_target, LOG_TARGET_AUTO, @@ -332,8 +358,10 @@ static int write_to_console( const char *func, const char *buffer) { - char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; - struct iovec iovec[6] = {}; + char location[256], + header_time[FORMAT_TIMESTAMP_MAX], + prefix[1 + DECIMAL_STR_MAX(int) + 2]; + struct iovec iovec[8] = {}; const char *on = NULL, *off = NULL; size_t n = 0; @@ -345,6 +373,13 @@ static int write_to_console( iovec[n++] = IOVEC_MAKE_STRING(prefix); } + if (show_time) { + if (format_timestamp(header_time, sizeof(header_time), now(CLOCK_REALTIME))) { + iovec[n++] = IOVEC_MAKE_STRING(header_time); + iovec[n++] = IOVEC_MAKE_STRING(" "); + } + } + if (show_color) get_log_colors(LOG_PRI(level), &on, &off, NULL); @@ -1099,22 +1134,32 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (log_show_location_from_string(value ?: "1") < 0) log_warning("Failed to parse log location setting '%s'. Ignoring.", value); + + } else if (proc_cmdline_key_streq(key, "systemd.log_time")) { + + if (log_show_time_from_string(value ?: "1") < 0) + log_warning("Failed to parse log time setting '%s'. Ignoring.", value); + } return 0; } void log_parse_environment_realm(LogRealm realm) { - /* Do not call from library code. */ - - const char *e; - if (getpid_cached() == 1 || get_ctty_devnr(0, NULL) < 0) /* Only try to read the command line in daemons. We assume that anything that has a * controlling tty is user stuff. For PID1 we do a special check in case it hasn't * closed the console yet. */ (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); + log_parse_environment_cli_realm(realm); +} + +void log_parse_environment_cli_realm(LogRealm realm) { + /* Do not call from library code. */ + + const char *e; + e = getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) log_warning("Failed to parse log target '%s'. Ignoring.", e); @@ -1130,6 +1175,10 @@ void log_parse_environment_realm(LogRealm realm) { e = getenv("SYSTEMD_LOG_LOCATION"); if (e && log_show_location_from_string(e) < 0) log_warning("Failed to parse log location '%s'. Ignoring.", e); + + e = getenv("SYSTEMD_LOG_TIME"); + if (e && log_show_time_from_string(e) < 0) + log_warning("Failed to parse log time '%s'. Ignoring.", e); } LogTarget log_get_target(void) { @@ -1156,6 +1205,14 @@ bool log_get_show_location(void) { return show_location; } +void log_show_time(bool b) { + show_time = b; +} + +bool log_get_show_time(void) { + return show_time; +} + int log_show_color_from_string(const char *e) { int t; @@ -1178,6 +1235,17 @@ int log_show_location_from_string(const char *e) { return 0; } +int log_show_time_from_string(const char *e) { + int t; + + t = parse_boolean(e); + if (t < 0) + return t; + + log_show_time(t); + return 0; +} + bool log_on_console(void) { if (IN_SET(log_target, LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE_PREFIXED)) @@ -1366,3 +1434,11 @@ void log_setup_service(void) { log_parse_environment(); (void) log_open(); } + +void log_setup_cli(void) { + /* Sets up logging the way it is most appropriate for running a program as a CLI utility. */ + + log_show_color(true); + log_parse_environment_cli(); + (void) log_open(); +} diff --git a/src/basic/log.h b/src/basic/log.h index 740fdbf61..15807d302 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -59,9 +59,12 @@ void log_show_color(bool b); bool log_get_show_color(void) _pure_; void log_show_location(bool b); bool log_get_show_location(void) _pure_; +void log_show_time(bool b); +bool log_get_show_time(void) _pure_; int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); +int log_show_time_from_string(const char *e); LogTarget log_get_target(void) _pure_; int log_get_max_level_realm(LogRealm realm) _pure_; @@ -81,8 +84,11 @@ void log_close(void); void log_forget_fds(void); void log_parse_environment_realm(LogRealm realm); +void log_parse_environment_cli_realm(LogRealm realm); #define log_parse_environment() \ log_parse_environment_realm(LOG_REALM) +#define log_parse_environment_cli() \ + log_parse_environment_cli_realm(LOG_REALM) int log_dispatch_internal( int level, @@ -338,3 +344,4 @@ int log_syntax_invalid_utf8_internal( #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) void log_setup_service(void); +void log_setup_cli(void); diff --git a/src/basic/macro.h b/src/basic/macro.h index 5aa7f59c0..ceea8176f 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -84,6 +84,14 @@ #define _variable_no_sanitize_address_ #endif +/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this + * unconditionally on llvm */ +#if defined(__clang__) +#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow"))) +#else +#define _function_no_sanitize_float_cast_overflow_ +#endif + /* Temporarily disable some warnings */ #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ @@ -114,6 +122,14 @@ _Pragma("GCC diagnostic push") #endif +#define DISABLE_WARNING_FLOAT_EQUAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") + +#define DISABLE_WARNING_TYPE_LIMITS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") @@ -270,6 +286,15 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ }) +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MIN(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) < (_B)) ? (_A) : (_B), \ + VOID_0)) + #define MIN3(x, y, z) \ ({ \ const typeof(x) _c = MIN(x, y); \ @@ -432,6 +457,8 @@ static inline int __coverity_check_and_return__(int condition) { #define char_array_0(x) x[sizeof(x)-1] = 0; +#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 @@ -451,8 +478,10 @@ static inline int __coverity_check_and_return__(int condition) { ans; \ }) +#define UPDATE_FLAG(orig, flag, b) \ + ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) #define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) + (v) = UPDATE_FLAG(v, flag, b) #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) @@ -509,6 +538,12 @@ static inline int __coverity_check_and_return__(int condition) { (y) = (_t); \ } while (false) +/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */ +#define FOREACH_POINTER(p, x, ...) \ + for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \ + p != (typeof(p)) (void*) -1; \ + p = *(++_l)) + /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local @@ -583,4 +618,17 @@ static inline int __coverity_check_and_return__(int condition) { DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \ DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func); +/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from + * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not + * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not + * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence + * memcpy() is great for our purposes. */ +#define READ_NOW(x) \ + ({ \ + typeof(x) _copy; \ + memcpy(&_copy, &(x), sizeof(_copy)); \ + asm volatile ("" : : : "memory"); \ + _copy; \ + }) + #include "log.h" diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index b7e2e67e8..4f596cffb 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -12,7 +12,8 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) -#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1)) +#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. */ static inline void memcpy_safe(void *dst, const void *src, size_t n) { diff --git a/src/basic/meson.build b/src/basic/meson.build index ccb22e159..90924d6cb 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -39,6 +39,7 @@ basic_sources = files(''' device-nodes.h dirent-util.c dirent-util.h + dlfcn-util.h efivars.c efivars.h env-file.c @@ -159,6 +160,8 @@ basic_sources = files(''' ordered-set.h parse-util.c parse-util.h + path-lookup.c + path-lookup.h path-util.c path-util.h prioq.c @@ -169,6 +172,7 @@ basic_sources = files(''' process-util.h procfs-util.c procfs-util.h + pthread-util.h quota-util.c quota-util.h random-util.c @@ -325,7 +329,8 @@ libbasic = static_library( threads, libcap, libselinux, - libm], + libm, + libdl], c_args : ['-fvisibility=default'], install : false) diff --git a/src/basic/missing_random.h b/src/basic/missing_random.h index 2e76031b3..17af87a3a 100644 --- a/src/basic/missing_random.h +++ b/src/basic/missing_random.h @@ -14,3 +14,7 @@ #ifndef GRND_RANDOM #define GRND_RANDOM 0x0002 #endif + +#ifndef GRND_INSECURE +#define GRND_INSECURE 0x0004 +#endif diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h index 276be366c..8c3fec1e3 100644 --- a/src/basic/missing_socket.h +++ b/src/basic/missing_socket.h @@ -62,3 +62,8 @@ struct sockaddr_vm { #ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif + +/* linux/sockios.h */ +#ifndef SIOCGSKNS +#define SIOCGSKNS 0x894C +#endif diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c index 0eba7fc51..e844a5980 100644 --- a/src/basic/mkdir-label.c +++ b/src/basic/mkdir-label.c @@ -10,6 +10,7 @@ #include "mkdir.h" #include "selinux-util.h" #include "smack-util.h" +#include "user-util.h" int mkdir_label(const char *path, mode_t mode) { int r; @@ -50,9 +51,9 @@ int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirF } int mkdir_parents_label(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir_label); + return mkdir_parents_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label); } int mkdir_p_label(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir_label); + return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label); } diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index fa682d4c4..6ebc2b95f 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -14,11 +14,18 @@ #include "stdio-util.h" #include "user-util.h" -int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) { +int mkdir_safe_internal( + const char *path, + mode_t mode, + uid_t uid, gid_t gid, + MkdirFlags flags, + mkdir_func_t _mkdir) { + struct stat st; int r; - assert(_mkdir != mkdir); + assert(path); + assert(_mkdir && _mkdir != mkdir); if (_mkdir(path, mode) >= 0) { r = chmod_and_chown(path, mode, uid, gid); @@ -44,19 +51,16 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd return -errno; } - if (!S_ISDIR(st.st_mode)) { - log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, - "Path \"%s\" already exists and is not a directory, refusing.", path); - return -ENOTDIR; - } + 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); if ((st.st_mode & 0007) > (mode & 0007) || (st.st_mode & 0070) > (mode & 0070) || - (st.st_mode & 0700) > (mode & 0700)) { - log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, - "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.", - path, st.st_mode & 0777, mode); - return -EEXIST; - } + (st.st_mode & 0700) > (mode & 0700)) + return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST), + "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.", + path, st.st_mode & 0777, mode); + if ((uid != UID_INVALID && st.st_uid != uid) || (gid != GID_INVALID && st.st_gid != gid)) { char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-"; @@ -65,10 +69,9 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd xsprintf(u, UID_FMT, uid); if (gid != UID_INVALID) xsprintf(g, GID_FMT, gid); - log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, - "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.", - path, st.st_uid, st.st_gid, u, g); - return -EEXIST; + return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST), + "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.", + path, st.st_uid, st.st_gid, u, g); } return 0; @@ -90,7 +93,7 @@ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags f return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper); } -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { +int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) { const char *p, *e; int r; @@ -133,34 +136,54 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk if (prefix && path_startswith(prefix, t)) continue; - r = _mkdir(t, mode); - if (r < 0 && r != -EEXIST) - return r; + if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) { + r = _mkdir(t, mode); + if (r < 0 && r != -EEXIST) + return r; + } else { + r = mkdir_safe_internal(t, mode, uid, gid, flags, _mkdir); + if (r < 0 && r != -EEXIST) + return r; + } } } int mkdir_parents(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper); + return mkdir_parents_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_errno_wrapper); } -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { +int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) { + return mkdir_parents_internal(prefix, path, mode, uid, gid, flags, mkdir_errno_wrapper); +} + +int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) { int r; /* Like mkdir -p */ assert(_mkdir != mkdir); - r = mkdir_parents_internal(prefix, path, mode, _mkdir); + r = mkdir_parents_internal(prefix, path, mode, uid, gid, flags, _mkdir); if (r < 0) return r; - r = _mkdir(path, mode); - if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0)) - return r; + if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) { + r = _mkdir(path, mode); + if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0)) + return r; + } else { + r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdir); + if (r < 0 && r != -EEXIST) + return r; + } return 0; } int mkdir_p(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper); + return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_errno_wrapper); +} + +int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) { + return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdir_errno_wrapper); } diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index eb54853ea..8bfaaf405 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -12,15 +12,17 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode); int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode); int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); int mkdir_parents(const char *path, mode_t mode); +int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); int mkdir_p(const char *path, mode_t mode); +int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); /* mandatory access control(MAC) versions */ int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags); -int mkdir_parents_label(const char *path, mode_t mode); +int mkdir_parents_label(const char *path, mode_t mod); int mkdir_p_label(const char *path, mode_t mode); /* internally used */ typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); +int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); +int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index c483b353c..df1f0ac34 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -338,6 +338,16 @@ bool fstype_is_api_vfs(const char *fstype) { "tracefs"); } +bool fstype_is_blockdev_backed(const char *fstype) { + const char *x; + + x = startswith(fstype, "fuse."); + if (x) + fstype = x; + + return !streq(fstype, "9p") && !fstype_is_network(fstype) && !fstype_is_api_vfs(fstype); +} + bool fstype_is_ro(const char *fstype) { /* All Linux file systems that are necessarily read-only */ return STR_IN_SET(fstype, diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index 5398836fe..ab4ed1939 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -14,6 +14,7 @@ int path_is_mount_point(const char *path, const char *root, int flags); bool fstype_is_network(const char *fstype); bool fstype_is_api_vfs(const char *fstype); +bool fstype_is_blockdev_backed(const char *fstype); bool fstype_is_ro(const char *fsype); bool fstype_can_discard(const char *fstype); bool fstype_can_uid_gid(const char *fstype); diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index b0168ae22..b34c53260 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -2,6 +2,7 @@ #include #include +#include #include "fd-util.h" #include "missing_fs.h" @@ -169,3 +170,16 @@ int fd_is_network_ns(int fd) { return r == CLONE_NEWNET; } + +int detach_mount_namespace(void) { + + /* Detaches the mount namespace, disabling propagation from our namespace to the host */ + + if (unshare(CLONE_NEWNS) < 0) + return -errno; + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + return -errno; + + return 0; +} diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index 8c17ce91b..99d9b977e 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -7,3 +7,5 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int * int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); int fd_is_network_ns(int fd); + +int detach_mount_namespace(void); diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 383a729ca..a42a57eb4 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -59,7 +59,7 @@ static inline void* ordered_set_steal_first(OrderedSet *s) { } static inline char **ordered_set_get_strv(OrderedSet *s) { - return internal_hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); + return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); } int ordered_set_consume(OrderedSet *s, void *p); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index a092a9c73..44f0438cf 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -24,9 +24,22 @@ int parse_boolean(const char *v) { if (!v) return -EINVAL; - if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) + if (STRCASE_IN_SET(v, + "1", + "yes", + "y", + "true", + "t", + "on")) return 1; - else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) + + if (STRCASE_IN_SET(v, + "0", + "no", + "n", + "false", + "f", + "off")) return 0; return -EINVAL; diff --git a/src/shared/path-lookup.c b/src/basic/path-lookup.c similarity index 84% rename from src/shared/path-lookup.c rename to src/basic/path-lookup.c index 48e0eec09..52968dee3 100644 --- a/src/shared/path-lookup.c +++ b/src/basic/path-lookup.c @@ -6,19 +6,15 @@ #include "alloc-util.h" #include "fs-util.h" -#include "install.h" #include "log.h" #include "macro.h" -#include "mkdir.h" #include "path-lookup.h" #include "path-util.h" -#include "rm-rf.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" #include "user-util.h" -#include "util.h" int xdg_user_runtime_dir(char **ret, const char *suffix) { const char *e; @@ -100,14 +96,14 @@ int xdg_user_data_dir(char **ret, const char *suffix) { static const char* const user_data_unit_paths[] = { "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, + USER_DATA_UNIT_DIR, "/usr/lib/systemd/user", "/usr/share/systemd/user", NULL }; static const char* const user_config_unit_paths[] = { - USER_CONFIG_UNIT_PATH, + USER_CONFIG_UNIT_DIR, "/etc/systemd/user", NULL }; @@ -325,12 +321,12 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru switch (scope) { case UNIT_FILE_SYSTEM: - a = strdup(SYSTEM_CONFIG_UNIT_PATH); + a = strdup(SYSTEM_CONFIG_UNIT_DIR); b = strdup("/run/systemd/system"); break; case UNIT_FILE_GLOBAL: - a = strdup(USER_CONFIG_UNIT_PATH); + a = strdup(USER_CONFIG_UNIT_DIR); b = strdup("/run/systemd/user"); break; @@ -480,6 +476,36 @@ static int patch_root_prefix_strv(char **l, const char *root_dir) { return 0; } +static int get_paths_from_environ(const char *var, char ***paths, bool *append) { + const char *e; + int r; + + assert(var); + assert(paths); + assert(append); + + *append = false; + + e = getenv(var); + if (e) { + const char *k; + + k = endswith(e, ":"); + if (k) { + e = strndupa(e, k - e); + *append = true; + } + + /* FIXME: empty components in other places should be rejected. */ + + r = path_split_and_make_absolute(e, paths); + if (r < 0) + return r; + } + + return 0; +} + int lookup_paths_init( LookupPaths *p, UnitFileScope scope, @@ -497,7 +523,6 @@ int lookup_paths_init( *persistent_attached = NULL, *runtime_attached = NULL; bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ _cleanup_strv_free_ char **paths = NULL; - const char *e; int r; assert(p); @@ -563,22 +588,9 @@ int lookup_paths_init( return r; /* First priority is whatever has been passed to us via env vars */ - e = getenv("SYSTEMD_UNIT_PATH"); - if (e) { - const char *k; - - k = endswith(e, ":"); - if (k) { - e = strndupa(e, k - e); - append = true; - } - - /* FIXME: empty components in other places should be rejected. */ - - r = path_split_and_make_absolute(e, &paths); - if (r < 0) - return r; - } + r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append); + if (r < 0) + return r; if (!paths || append) { /* Let's figure something out. */ @@ -602,7 +614,7 @@ int lookup_paths_init( STRV_IFNOTNULL(transient), STRV_IFNOTNULL(generator_early), persistent_config, - SYSTEM_CONFIG_UNIT_PATH, + SYSTEM_CONFIG_UNIT_DIR, "/etc/systemd/system", STRV_IFNOTNULL(persistent_attached), runtime_config, @@ -626,7 +638,7 @@ int lookup_paths_init( STRV_IFNOTNULL(transient), STRV_IFNOTNULL(generator_early), persistent_config, - USER_CONFIG_UNIT_PATH, + USER_CONFIG_UNIT_DIR, "/etc/systemd/user", runtime_config, "/run/systemd/user", @@ -634,7 +646,7 @@ int lookup_paths_init( "/usr/local/share/systemd/user", "/usr/share/systemd/user", "/usr/local/lib/systemd/user", - USER_DATA_UNIT_PATH, + USER_DATA_UNIT_DIR, "/usr/lib/systemd/user", STRV_IFNOTNULL(generator_late)); break; @@ -767,74 +779,91 @@ void lookup_paths_log(LookupPaths *p) { } } -int lookup_paths_mkdir_generator(LookupPaths *p) { - int r, q; - - assert(p); - - if (!p->generator || !p->generator_early || !p->generator_late) - return -EINVAL; - - r = mkdir_p_label(p->generator, 0755); - - q = mkdir_p_label(p->generator_early, 0755); - if (q < 0 && r >= 0) - r = q; - - q = mkdir_p_label(p->generator_late, 0755); - if (q < 0 && r >= 0) - r = q; - - return r; -} - -void lookup_paths_trim_generator(LookupPaths *p) { - assert(p); - - /* Trim empty dirs */ - - if (p->generator) - (void) rmdir(p->generator); - if (p->generator_early) - (void) rmdir(p->generator_early); - if (p->generator_late) - (void) rmdir(p->generator_late); -} - -void lookup_paths_flush_generator(LookupPaths *p) { - assert(p); - - /* Flush the generated unit files in full */ - - if (p->generator) - (void) rm_rf(p->generator, REMOVE_ROOT|REMOVE_PHYSICAL); - if (p->generator_early) - (void) rm_rf(p->generator_early, REMOVE_ROOT|REMOVE_PHYSICAL); - if (p->generator_late) - (void) rm_rf(p->generator_late, REMOVE_ROOT|REMOVE_PHYSICAL); - - if (p->temporary_dir) - (void) rm_rf(p->temporary_dir, REMOVE_ROOT|REMOVE_PHYSICAL); -} - char **generator_binary_paths(UnitFileScope scope) { + bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */ + _cleanup_strv_free_ char **paths = NULL; + int r; - switch (scope) { + /* First priority is whatever has been passed to us via env vars */ + r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append); + if (r < 0) + return NULL; - case UNIT_FILE_SYSTEM: - return strv_new("/run/systemd/system-generators", - "/etc/systemd/system-generators", - "/usr/local/lib/systemd/system-generators", - SYSTEM_GENERATOR_PATH); + if (!paths || append) { + _cleanup_strv_free_ char **add = NULL; - case UNIT_FILE_GLOBAL: - case UNIT_FILE_USER: - return strv_new("/run/systemd/user-generators", - "/etc/systemd/user-generators", - "/usr/local/lib/systemd/user-generators", - USER_GENERATOR_PATH); + switch (scope) { - default: - assert_not_reached("Hmm, unexpected scope."); + case UNIT_FILE_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: + add = strv_new("/run/systemd/user-generators", + "/etc/systemd/user-generators", + "/usr/local/lib/systemd/user-generators", + USER_GENERATOR_DIR); + break; + + default: + assert_not_reached("Hmm, unexpected scope."); + } + + if (!add) + return NULL; + + if (paths) { + r = strv_extend_strv(&paths, add, true); + if (r < 0) + return NULL; + } else + /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, + * and don't have to copy anything */ + paths = TAKE_PTR(add); } + + return TAKE_PTR(paths); +} + +char **env_generator_binary_paths(bool is_system) { + bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */ + _cleanup_strv_free_ char **paths = NULL; + _cleanup_strv_free_ char **add = NULL; + int r; + + /* First priority is whatever has been passed to us via env vars */ + r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append); + if (r < 0) + return NULL; + + if (!paths || append) { + if (is_system) + add = strv_new("/run/systemd/system-environment-generators", + "/etc/systemd/system-environment-generators", + "/usr/local/lib/systemd/system-environment-generators", + SYSTEM_ENV_GENERATOR_DIR); + else + add = strv_new("/run/systemd/user-environment-generators", + "/etc/systemd/user-environment-generators", + "/usr/local/lib/systemd/user-environment-generators", + USER_ENV_GENERATOR_DIR); + + if (!add) + return NULL; + } + + if (paths) { + r = strv_extend_strv(&paths, add, true); + if (r < 0) + return NULL; + } else + /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, + * and don't have to copy anything */ + paths = TAKE_PTR(add); + + return TAKE_PTR(paths); } diff --git a/src/shared/path-lookup.h b/src/basic/path-lookup.h similarity index 92% rename from src/shared/path-lookup.h rename to src/basic/path-lookup.h index f0762d248..ae37f9feb 100644 --- a/src/shared/path-lookup.h +++ b/src/basic/path-lookup.h @@ -5,6 +5,7 @@ typedef struct LookupPaths LookupPaths; +#include "def.h" #include "unit-file.h" #include "macro.h" @@ -64,11 +65,10 @@ bool path_is_user_data_dir(const char *path); bool path_is_user_config_dir(const char *path); void lookup_paths_log(LookupPaths *p); - -int lookup_paths_mkdir_generator(LookupPaths *p); -void lookup_paths_trim_generator(LookupPaths *p); -void lookup_paths_flush_generator(LookupPaths *p); - void lookup_paths_free(LookupPaths *p); char **generator_binary_paths(UnitFileScope scope); +char **env_generator_binary_paths(bool is_system); + +#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) +#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network") diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 986dfe94a..c4e022b3a 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -640,10 +640,8 @@ int find_binary(const char *name, char **ret) { if (access(j, X_OK) >= 0) { /* Found it! */ - if (ret) { - *ret = path_simplify(j, false); - j = NULL; - } + if (ret) + *ret = path_simplify(TAKE_PTR(j), false); return 0; } diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 6b1fb13d0..ba47ca581 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -118,16 +118,19 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF assert(parse_item); /* We parse the EFI variable first, because later settings have higher priority. */ - r = systemd_efi_options_variable(&line); - if (r < 0) { - if (r != -ENODATA) - log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m"); - } else { - r = proc_cmdline_parse_given(line, parse_item, data, flags); - if (r < 0) - return r; - line = mfree(line); + if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) { + r = systemd_efi_options_variable(&line); + if (r < 0) { + if (r != -ENODATA) + log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m"); + } else { + r = proc_cmdline_parse_given(line, parse_item, data, flags); + if (r < 0) + return r; + + line = mfree(line); + } } r = proc_cmdline(&line); @@ -220,7 +223,7 @@ static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags f } int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) { - _cleanup_free_ char *line = NULL; + _cleanup_free_ char *line = NULL, *v = NULL; int r; /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable. @@ -247,14 +250,27 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val if (r < 0) return r; - r = cmdline_get_key(line, key, flags, ret_value); - if (r != 0) /* Either error or true if found. */ + if (FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) /* Shortcut */ + return cmdline_get_key(line, key, flags, ret_value); + + r = cmdline_get_key(line, key, flags, ret_value ? &v : NULL); + if (r < 0) return r; + if (r > 0) { + if (ret_value) + *ret_value = TAKE_PTR(v); + + return r; + } line = mfree(line); r = systemd_efi_options_variable(&line); - if (r == -ENODATA) + if (r == -ENODATA) { + if (ret_value) + *ret_value = NULL; + return false; /* Not found */ + } if (r < 0) return r; @@ -270,17 +286,17 @@ int proc_cmdline_get_bool(const char *key, bool *ret) { r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v); if (r < 0) return r; - if (r == 0) { + if (r == 0) { /* key not specified at all */ *ret = false; return 0; } - if (v) { /* parameter passed */ + if (v) { /* key with parameter passed */ r = parse_boolean(v); if (r < 0) return r; *ret = r; - } else /* no parameter passed */ + } else /* key without parameter passed */ *ret = true; return 1; @@ -288,6 +304,7 @@ int proc_cmdline_get_bool(const char *key, bool *ret) { int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) { _cleanup_free_ char *line = NULL; + bool processing_efi = true; const char *p; va_list ap; int r, ret = 0; @@ -298,9 +315,11 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) { /* This call may clobber arguments on failure! */ - r = proc_cmdline(&line); - if (r < 0) - return r; + if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) { + r = systemd_efi_options_variable(&line); + if (r < 0 && r != -ENODATA) + log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m"); + } p = line; for (;;) { @@ -309,8 +328,22 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) { r = proc_cmdline_extract_first(&p, &word, flags); if (r < 0) return r; - if (r == 0) + if (r == 0) { + /* We finished with this command line. If this was the EFI one, then let's proceed with the regular one */ + if (processing_efi) { + processing_efi = false; + + line = mfree(line); + r = proc_cmdline(&line); + if (r < 0) + return r; + + p = line; + continue; + } + break; + } va_start(ap, flags); diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h index 4115fdbc9..077d3a99f 100644 --- a/src/basic/proc-cmdline.h +++ b/src/basic/proc-cmdline.h @@ -6,9 +6,10 @@ #include "log.h" typedef enum ProcCmdlineFlags { - PROC_CMDLINE_STRIP_RD_PREFIX = 1 << 0, - PROC_CMDLINE_VALUE_OPTIONAL = 1 << 1, - PROC_CMDLINE_RD_STRICT = 1 << 2, + PROC_CMDLINE_STRIP_RD_PREFIX = 1 << 0, /* automatically strip "rd." prefix if it is set (and we are in the initrd, since otherwise we'd not consider it anyway) */ + PROC_CMDLINE_VALUE_OPTIONAL = 1 << 1, /* the value is optional (for boolean switches that can omit the value) */ + PROC_CMDLINE_RD_STRICT = 1 << 2, /* ignore this in the initrd */ + PROC_CMDLINE_IGNORE_EFI_OPTIONS = 1 << 3, /* don't check systemd's private EFI variable */ } ProcCmdlineFlags; typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 0a1dac758..80f13048c 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -206,50 +206,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } -int rename_process(const char name[]) { - static size_t mm_size = 0; - static char *mm = NULL; - bool truncated = false; - size_t l; +static int update_argv(const char name[], size_t l) { + static int can_do = -1; - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ + if (can_do == 0) + return 0; + can_do = false; /* We'll set it to true only if the whole process works */ /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is @@ -257,22 +219,29 @@ int rename_process(const char name[]) { * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but * mmap() is not. */ if (geteuid() != 0) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Skipping PR_SET_MM, as we don't have privileges."); + + static size_t mm_size = 0; + static char *mm = NULL; + int r; + + if (mm_size < l+1) { size_t nn_size; char *nn; nn_size = PAGE_ALIGN(l+1); nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } + if (nn == MAP_FAILED) + return log_debug_errno(errno, "mmap() failed: %m"); strncpy(nn, name, nn_size); /* Now, let's tell the kernel about this new memory */ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + if (ERRNO_IS_PRIVILEGE(errno)) + return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be * below the desired start address, in which case the kernel may have kicked this back due * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in @@ -284,15 +253,13 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); (void) munmap(nn, nn_size); - goto use_saved_argv; + return r; } - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - goto use_saved_argv; - } + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) + return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); } else { /* And update the end pointer to the new end, too. If this fails, we don't really know what * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, @@ -314,13 +281,56 @@ int rename_process(const char name[]) { log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } -use_saved_argv: + can_do = true; + return 0; +} + +int rename_process(const char name[]) { + bool truncated = false; + + /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's + * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in + * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; + * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be + * truncated. + * + * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ + + if (isempty(name)) + return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + + size_t l = strlen(name); + + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); + if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ + truncated = true; + + /* Second step, change glibc's ID of the process name. */ + if (program_invocation_name) { + size_t k; + + k = strlen(program_invocation_name); + strncpy(program_invocation_name, name, k); + if (l > k) + truncated = true; + } + + /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but + * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at + * the end. This is the best option for changing /proc/self/cmdline. */ + (void) update_argv(name, l); + /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if * it still looks here */ - if (saved_argc > 0) { - int i; - if (saved_argv[0]) { size_t k; @@ -330,7 +340,7 @@ use_saved_argv: truncated = true; } - for (i = 1; i < saved_argc; i++) { + for (int i = 1; i < saved_argc; i++) { if (!saved_argv[i]) break; @@ -628,6 +638,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) { return 0; } +int get_process_umask(pid_t pid, mode_t *umask) { + _cleanup_free_ char *m = NULL; + const char *p; + int r; + + assert(umask); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "Umask", WHITESPACE, &m); + if (r == -ENOENT) + return -ESRCH; + + return parse_mode(m, umask); +} + int wait_for_terminate(pid_t pid, siginfo_t *status) { siginfo_t dummy; @@ -1178,6 +1205,11 @@ int must_be_root(void) { return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); } +static void restore_sigsetp(sigset_t **ssp) { + if (*ssp) + (void) sigprocmask(SIG_SETMASK, *ssp, NULL); +} + int safe_fork_full( const char *name, const int except_fds[], @@ -1187,7 +1219,8 @@ int safe_fork_full( pid_t original_pid, pid; sigset_t saved_ss, ss; - bool block_signals = false; + _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; + bool block_signals = false, block_all = false; int prio, r; /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always @@ -1202,7 +1235,7 @@ int safe_fork_full( * be sure that SIGTERMs are not lost we might send to the child. */ assert_se(sigfillset(&ss) >= 0); - block_signals = true; + block_signals = block_all = true; } else if (flags & FORK_WAIT) { /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ @@ -1212,28 +1245,31 @@ int safe_fork_full( block_signals = true; } - if (block_signals) + if (block_signals) { if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) return log_full_errno(prio, errno, "Failed to set signal mask: %m"); + saved_ssp = &saved_ss; + } if (flags & FORK_NEW_MOUNTNS) pid = raw_clone(SIGCHLD|CLONE_NEWNS); else pid = fork(); - if (pid < 0) { - r = -errno; - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - return log_full_errno(prio, r, "Failed to fork: %m"); - } + if (pid < 0) + return log_full_errno(prio, errno, "Failed to fork: %m"); if (pid > 0) { /* We are in the parent process */ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); if (flags & FORK_WAIT) { + if (block_all) { + /* undo everything except SIGCHLD */ + ss = saved_ss; + assert_se(sigaddset(&ss, SIGCHLD) >= 0); + (void) sigprocmask(SIG_SETMASK, &ss, NULL); + } + r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); if (r < 0) return r; @@ -1241,9 +1277,6 @@ int safe_fork_full( return -EPROTO; } - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - if (ret_pid) *ret_pid = pid; @@ -1252,6 +1285,9 @@ int safe_fork_full( /* We are in the child process */ + /* Restore signal mask manually */ + saved_ssp = NULL; + if (flags & FORK_REOPEN_LOG) { /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ log_close(); @@ -1298,7 +1334,7 @@ int safe_fork_full( ppid = getppid(); if (ppid == 0) - /* Parent is in a differn't PID namespace. */; + /* Parent is in a different PID namespace. */; else if (ppid != original_pid) { log_debug("Parent died early, raising SIGTERM."); (void) raise(SIGTERM); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 5116d7966..49bb74ac0 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -20,14 +20,16 @@ #define procfs_file_alloca(pid, field) \ ({ \ pid_t _pid_ = (pid); \ - const char *_r_; \ + const char *_field_ = (field); \ + char *_r_; \ if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ + _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \ + strcpy(stpcpy(_r_, "/proc/self/"), _field_); \ } else { \ - _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ + _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \ + sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \ } \ - _r_; \ + (const char*) _r_; \ }) typedef enum ProcessCmdlineFlags { @@ -45,6 +47,7 @@ int get_process_cwd(pid_t pid, char **cwd); int get_process_root(pid_t pid, char **root); int get_process_environ(pid_t pid, char **environ); int get_process_ppid(pid_t pid, pid_t *ppid); +int get_process_umask(pid_t pid, mode_t *umask); int wait_for_terminate(pid_t pid, siginfo_t *status); diff --git a/src/basic/pthread-util.h b/src/basic/pthread-util.h new file mode 100644 index 000000000..dc3eaba43 --- /dev/null +++ b/src/basic/pthread-util.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "macro.h" + +static inline pthread_mutex_t* pthread_mutex_lock_assert(pthread_mutex_t *mutex) { + assert_se(pthread_mutex_lock(mutex) == 0); + return mutex; +} + +static inline void pthread_mutex_unlock_assertp(pthread_mutex_t **mutexp) { + if (*mutexp) + assert_se(pthread_mutex_unlock(*mutexp) == 0); +} diff --git a/src/basic/random-util.c b/src/basic/random-util.c index 8717a49ba..6eeed9af3 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #if HAVE_SYS_AUXV_H @@ -75,7 +77,7 @@ int rdrand(unsigned long *ret) { * 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 to trigger many collisions to exploit it for the purpose + * 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. * @@ -208,7 +210,9 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { for (;;) { - r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK); + r = getrandom(p, n, + (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | + (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); if (r > 0) { have_syscall = true; @@ -264,6 +268,18 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { /* 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; + } + + return -errno; } else return -errno; } @@ -326,9 +342,11 @@ void initialize_srand(void) { /* INT_MAX gives us only 31 bits, so use 24 out of that. */ #if RAND_MAX >= INT_MAX +assert_cc(RAND_MAX >= 16777215); # define RAND_STEP 3 #else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ +/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */ +assert_cc(RAND_MAX >= 255); # define RAND_STEP 1 #endif @@ -393,7 +411,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) >= 0) + if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ @@ -422,3 +440,36 @@ size_t random_pool_size(void) { /* Use the minimum as default, if we can't retrieve the correct value */ return RANDOM_POOL_SIZE_MIN; } + +int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { + int r; + + assert(fd >= 0); + assert(seed && size > 0); + + if (credit) { + _cleanup_free_ struct rand_pool_info *info = NULL; + + /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any + * chance for confusion here. */ + if (size > INT_MAX / 8) + return -EOVERFLOW; + + info = malloc(offsetof(struct rand_pool_info, buf) + size); + if (!info) + return -ENOMEM; + + info->entropy_count = size * 8; + info->buf_size = size; + memcpy(info->buf, seed, size); + + if (ioctl(fd, RNDADDENTROPY, info) < 0) + return -errno; + } else { + r = loop_write(fd, seed, size, false); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/basic/random-util.h b/src/basic/random-util.h index facc11b97..7824fface 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -10,6 +10,7 @@ typedef enum RandomFlags { 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 */ } 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 */ @@ -37,3 +38,5 @@ int rdrand(unsigned long *ret); #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) size_t random_pool_size(void); + +int random_write_entropy(int fd, const void *seed, size_t size, bool credit); diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 1095cb426..c94ee26bd 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -12,6 +12,7 @@ #include #if HAVE_SELINUX +#include #include #include #include @@ -31,17 +32,33 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); #define _cleanup_context_free_ _cleanup_(context_freep) +static int mac_selinux_reload(int seqno); + static int cached_use = -1; static struct selabel_handle *label_hnd = NULL; -#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, __VA_ARGS__) -#define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__) +#define log_enforcing(...) \ + log_full(security_getenforce() != 0 ? LOG_ERR : LOG_WARNING, __VA_ARGS__) + +#define log_enforcing_errno(error, ...) \ + ({ \ + bool _enforcing = security_getenforce() != 0; \ + int _level = _enforcing ? LOG_ERR : LOG_WARNING; \ + int _e = (error); \ + \ + int _r = (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_internal_realm(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + : -ERRNO_VALUE(_e); \ + _enforcing ? _r : 0; \ + }) #endif bool mac_selinux_use(void) { #if HAVE_SELINUX - if (cached_use < 0) + if (_unlikely_(cached_use < 0)) { cached_use = is_selinux_enabled() > 0; + log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled"); + } return cached_use; #else @@ -56,11 +73,13 @@ void mac_selinux_retest(void) { } int mac_selinux_init(void) { - int r = 0; - #if HAVE_SELINUX usec_t before_timestamp, after_timestamp; struct mallinfo before_mallinfo, after_mallinfo; + char timespan[FORMAT_TIMESPAN_MAX]; + int l; + + selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload); if (label_hnd) return 0; @@ -72,25 +91,20 @@ int mac_selinux_init(void) { before_timestamp = now(CLOCK_MONOTONIC); label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); - if (!label_hnd) { - log_enforcing_errno(errno, "Failed to initialize SELinux context: %m"); - r = security_getenforce() == 1 ? -errno : 0; - } else { - char timespan[FORMAT_TIMESPAN_MAX]; - int l; + if (!label_hnd) + return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m"); - after_timestamp = now(CLOCK_MONOTONIC); - after_mallinfo = mallinfo(); + after_timestamp = now(CLOCK_MONOTONIC); + after_mallinfo = mallinfo(); - l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; + l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; + + log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.", + format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0), + (l+1023)/1024); - log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.", - format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0), - (l+1023)/1024); - } #endif - - return r; + return 0; } void mac_selinux_finish(void) { @@ -104,13 +118,12 @@ void mac_selinux_finish(void) { #endif } -void mac_selinux_reload(void) { - #if HAVE_SELINUX +static int mac_selinux_reload(int seqno) { struct selabel_handle *backup_label_hnd; if (!label_hnd) - return; + return 0; backup_label_hnd = TAKE_PTR(label_hnd); @@ -121,10 +134,12 @@ void mac_selinux_reload(void) { selabel_close(backup_label_hnd); else label_hnd = backup_label_hnd; -#endif -} -int mac_selinux_fix(const char *path, LabelFixFlags flags) { + return 0; +} +#endif + +int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) { #if HAVE_SELINUX char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; @@ -151,7 +166,10 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) { if (fstat(fd, &st) < 0) return -errno; - if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) { + /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ + (void) avc_netlink_check_nb(); + + if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) { r = -errno; /* If there's no label to set, then exit without warning */ @@ -185,9 +203,7 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) { return 0; fail: - log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path); - if (security_getenforce() == 1) - return r; + return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", path, inside_path); #endif return 0; @@ -202,21 +218,17 @@ int mac_selinux_apply(const char *path, const char *label) { assert(path); assert(label); - if (setfilecon(path, label) < 0) { - log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path); - if (security_getenforce() > 0) - return -errno; - } + if (setfilecon(path, label) < 0) + return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path); #endif return 0; } int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { - int r = -EOPNOTSUPP; - #if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; security_class_t sclass; + int r; assert(exe); assert(label); @@ -239,36 +251,39 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { r = security_compute_create_raw(mycon, fcon, sclass, label); if (r < 0) return -errno; -#endif - return r; + return 0; +#else + return -EOPNOTSUPP; +#endif } int mac_selinux_get_our_label(char **label) { - int r = -EOPNOTSUPP; +#if HAVE_SELINUX + int r; assert(label); -#if HAVE_SELINUX if (!mac_selinux_use()) return -EOPNOTSUPP; r = getcon_raw(label); if (r < 0) return -errno; -#endif - return r; + return 0; +#else + return -EOPNOTSUPP; +#endif } int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { - int r = -EOPNOTSUPP; - #if HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; security_class_t sclass; const char *range = NULL; + int r; assert(socket_fd >= 0); assert(exe); @@ -321,21 +336,19 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * r = security_compute_create_raw(mycon, fcon, sclass, label); if (r < 0) return -errno; -#endif - return r; + return 0; +#else + return -EOPNOTSUPP; +#endif } char* mac_selinux_free(char *label) { #if HAVE_SELINUX - if (!label) - return NULL; - - if (!mac_selinux_use()) - return NULL; - freecon(label); +#else + assert(!label); #endif return NULL; @@ -349,32 +362,30 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) assert(abspath); assert(path_is_absolute(abspath)); + /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ + (void) avc_netlink_check_nb(); + r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); if (r < 0) { /* No context specified by the policy? Proceed without setting it. */ if (errno == ENOENT) return 0; - log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); - } else { - if (setfscreatecon_raw(filecon) >= 0) - return 0; /* Success! */ - - log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); + return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); } - if (security_getenforce() > 0) - return -errno; + if (setfscreatecon_raw(filecon) < 0) + return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); return 0; } #endif int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) { - int r = 0; - #if HAVE_SELINUX _cleanup_free_ char *abspath = NULL; + int r; + assert(path); @@ -396,15 +407,16 @@ int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) return -ENOMEM; } - r = selinux_create_file_prepare_abspath(path, mode); + return selinux_create_file_prepare_abspath(path, mode); +#else + return 0; #endif - return r; } int mac_selinux_create_file_prepare(const char *path, mode_t mode) { - int r = 0; - #if HAVE_SELINUX + int r; + _cleanup_free_ char *abspath = NULL; assert(path); @@ -416,9 +428,10 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) { if (r < 0) return r; - r = selinux_create_file_prepare_abspath(abspath, mode); + return selinux_create_file_prepare_abspath(abspath, mode); +#else + return 0; #endif - return r; } void mac_selinux_create_file_clear(void) { @@ -436,17 +449,13 @@ void mac_selinux_create_file_clear(void) { int mac_selinux_create_socket_prepare(const char *label) { #if HAVE_SELINUX + assert(label); + if (!mac_selinux_use()) return 0; - assert(label); - - if (setsockcreatecon(label) < 0) { - log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label); - - if (security_getenforce() == 1) - return -errno; - } + if (setsockcreatecon(label) < 0) + return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label); #endif return 0; @@ -497,6 +506,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); + /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ + (void) avc_netlink_check_nb(); + if (path_is_absolute(path)) r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); else { @@ -514,15 +526,14 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { if (errno == ENOENT) goto skipped; - log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); - if (security_getenforce() > 0) - return -errno; - + r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); + if (r < 0) + return r; } else { if (setfscreatecon_raw(fcon) < 0) { - log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path); - if (security_getenforce() > 0) - return -errno; + r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path); + if (r < 0) + return r; } else context_changed = true; } @@ -530,7 +541,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { r = bind(fd, addr, addrlen) < 0 ? -errno : 0; if (context_changed) - setfscreatecon_raw(NULL); + (void) setfscreatecon_raw(NULL); return r; diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index b73b7c50e..d053b00b5 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -20,9 +20,12 @@ void mac_selinux_retest(void); int mac_selinux_init(void); void mac_selinux_finish(void); -void mac_selinux_reload(void); -int mac_selinux_fix(const char *path, LabelFixFlags flags); +int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags); +static inline int mac_selinux_fix(const char *path, LabelFixFlags flags) { + return mac_selinux_fix_container(path, path, flags); +} + int mac_selinux_apply(const char *path, const char *label); int mac_selinux_get_create_label_from_exe(const char *exe, char **label); diff --git a/src/basic/set.h b/src/basic/set.h index 5f1956177..e4fc1e3c4 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -5,40 +5,46 @@ #include "hashmap.h" #include "macro.h" -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define set_free_and_replace(a, b) \ + ({ \ + set_free(a); \ + (a) = (b); \ + (b) = NULL; \ + 0; \ + }) + +Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL); } static inline Set *set_free_free(Set *s) { - return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL); + return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL); } /* no set_free_free_free */ -static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); -} +#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ /* no set_replace */ static inline void *set_get(const Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE((Set *) s), key); + return _hashmap_get(HASHMAP_BASE((Set *) s), key); } /* no set_get2 */ static inline bool set_contains(const Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key); + return _hashmap_contains(HASHMAP_BASE((Set *) s), key); } static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); + return _hashmap_remove(HASHMAP_BASE(s), key); } /* no set_remove2 */ @@ -48,19 +54,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key); int set_merge(Set *s, Set *other); static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); + return _hashmap_reserve(HASHMAP_BASE(h), entries_add); } static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); + return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); } static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); + return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); } static inline unsigned set_size(const Set *s) { - return internal_hashmap_size(HASHMAP_BASE((Set *) s)); + return _hashmap_size(HASHMAP_BASE((Set *) s)); } static inline bool set_isempty(const Set *s) { @@ -68,23 +74,23 @@ static inline bool set_isempty(const Set *s) { } static inline unsigned set_buckets(const Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE((Set *) s)); + return _hashmap_buckets(HASHMAP_BASE((Set *) s)); } bool set_iterate(const Set *s, Iterator *i, void **value); static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); + _hashmap_clear(HASHMAP_BASE(s), NULL, NULL); } static inline void set_clear_free(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s), free, NULL); + _hashmap_clear(HASHMAP_BASE(s), free, NULL); } /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -103,18 +109,28 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(const Set *s) { - return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); + return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL); } /* no set_next */ static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); + return _hashmap_get_strv(HASHMAP_BASE(s)); } +int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS); +#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) + +int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key HASHMAP_DEBUG_PARAMS); +#define set_ensure_consume(s, hash_ops, key) _set_ensure_consume(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) + int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); + +int _set_put_strdup(Set **s, const char *p HASHMAP_DEBUG_PARAMS); +#define set_put_strdup(s, p) _set_put_strdup(s, p HASHMAP_DEBUG_SRC_ARGS) +int _set_put_strdupv(Set **s, char **l HASHMAP_DEBUG_PARAMS); +#define set_put_strdupv(s, l) _set_put_strdupv(s, l HASHMAP_DEBUG_SRC_ARGS) + int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); #define SET_FOREACH(e, s, i) \ diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 666431aa3..61180819b 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -151,12 +151,6 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { } } -void siphash24_compress_boolean(bool in, struct siphash *state) { - int i = in; - - siphash24_compress(&i, sizeof i, state); -} - uint64_t siphash24_finalize(struct siphash *state) { uint64_t b; diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index 1937fea29..7f799ede3 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -17,9 +17,21 @@ struct siphash { void siphash24_init(struct siphash *state, const uint8_t k[static 16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -void siphash24_compress_boolean(bool in, struct siphash *state); #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) +static inline void siphash24_compress_boolean(bool in, struct siphash *state) { + uint8_t i = in; + + siphash24_compress(&i, sizeof i, state); +} + +static inline void siphash24_compress_string(const char *in, struct siphash *state) { + if (!in) + return; + + siphash24_compress(in, strlen(in), state); +} + uint64_t siphash24_finalize(struct siphash *state); uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[static 16]); diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c index da9a2139d..8043a97c3 100644 --- a/src/basic/smack-util.c +++ b/src/basic/smack-util.c @@ -206,7 +206,7 @@ int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) { return smack_fix_fd(fd, path, flags); } -int mac_smack_fix(const char *path, LabelFixFlags flags) { +int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) { _cleanup_free_ char *abspath = NULL; _cleanup_close_ int fd = -1; int r; @@ -228,7 +228,7 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) { return -errno; } - return smack_fix_fd(fd, abspath, flags); + return smack_fix_fd(fd, inside_path, flags); } int mac_smack_copy(const char *dest, const char *src) { @@ -274,7 +274,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) { return 0; } -int mac_smack_fix(const char *path, LabelFixFlags flags) { +int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) { return 0; } diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h index 395ec07b5..df2ce3707 100644 --- a/src/basic/smack-util.h +++ b/src/basic/smack-util.h @@ -29,7 +29,11 @@ typedef enum SmackAttr { bool mac_smack_use(void); -int mac_smack_fix(const char *path, LabelFixFlags flags); +int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags); +static inline int mac_smack_fix(const char *path, LabelFixFlags flags) { + return mac_smack_fix_container(path, path, flags); +} + int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags); const char* smack_attr_to_string(SmackAttr i) _const_; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index b797a5218..fb1265985 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -21,6 +21,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "io-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" @@ -104,7 +105,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) { if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) return -EINVAL; } else { - /* If there's no embedded NUL byte, then then the size needs to match the whole + /* If there's no embedded NUL byte, then the size needs to match the whole * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful, * and considers both equivalent: getsockname() even extends sockaddr_un beyond its * size if the path is non NUL terminated.)*/ @@ -818,10 +819,7 @@ ssize_t send_one_fd_iov_sa( const struct sockaddr *sa, socklen_t len, int flags) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {}; struct msghdr mh = { .msg_name = (struct sockaddr*) sa, .msg_namelen = len, @@ -850,8 +848,6 @@ ssize_t send_one_fd_iov_sa( cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); } k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); if (k < 0) @@ -877,17 +873,14 @@ ssize_t receive_one_fd_iov( int flags, int *ret_fd) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; struct msghdr mh = { .msg_control = &control, .msg_controllen = sizeof(control), .msg_iov = iov, .msg_iovlen = iovlen, }; - struct cmsghdr *cmsg, *found = NULL; + struct cmsghdr *found; ssize_t k; assert(transport_fd >= 0); @@ -905,16 +898,7 @@ ssize_t receive_one_fd_iov( if (k < 0) return k; - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - + found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); if (!found) { cmsg_close_all(&mh); @@ -985,10 +969,6 @@ fallback: int flush_accept(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; int r, b; socklen_t l = sizeof(b); @@ -1009,12 +989,12 @@ int flush_accept(int fd) { for (unsigned iteration = 0;; iteration++) { int cfd; - r = poll(&pollfd, 1, 0); + r = fd_wait_for_event(fd, POLLIN, 0); if (r < 0) { - if (errno == EINTR) + if (r == -EINTR) continue; - return -errno; + return r; } if (r == 0) return 0; @@ -1150,6 +1130,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) { int socket_bind_to_ifindex(int fd, int ifindex) { char ifname[IF_NAMESIZE + 1]; + int r; assert(fd >= 0); @@ -1161,10 +1142,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) { return 0; } - if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0) - return 0; - if (errno != ENOPROTOOPT) - return -errno; + r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); + if (r != -ENOPROTOOPT) + return r; /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ if (!format_ifname(ifindex, ifname)) @@ -1191,5 +1171,27 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) { } return n; - +} + +int socket_pass_pktinfo(int fd, bool b) { + int af; + socklen_t sl = sizeof(af); + + if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) + return -errno; + + switch (af) { + + case AF_INET: + return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); + + case AF_INET6: + return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); + + case AF_NETLINK: + return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); + + default: + return -EAFNOSUPPORT; + } } diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 95643135d..9e02e3988 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -158,6 +158,25 @@ int flush_accept(int fd); struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); +/* Type-safe, dereferencing version of cmsg_find() */ +#define CMSG_FIND_DATA(mh, level, type, ctype) \ + ({ \ + struct cmsghdr *_found; \ + _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \ + (ctype*) (_found ? CMSG_DATA(_found) : NULL); \ + }) + +/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type + * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr" + * structures. */ +#define CMSG_BUFFER_TYPE(size) \ + union { \ + struct cmsghdr cmsghdr; \ + uint8_t buf[size]; \ + uint8_t align_check[(size) >= CMSG_SPACE(0) && \ + (size) == CMSG_ALIGN(size) ? 1 : -1]; \ + } + /* * Certain hardware address types (e.g Infiniband) do not fit into sll_addr * (8 bytes) and run over the structure. This macro returns the correct size that @@ -201,3 +220,5 @@ int socket_bind_to_ifname(int fd, const char *ifname); int socket_bind_to_ifindex(int fd, int ifindex); ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); + +int socket_pass_pktinfo(int fd, bool b); diff --git a/src/basic/sort-util.h b/src/basic/sort-util.h index e029f8646..a8dc3bb6e 100644 --- a/src/basic/sort-util.h +++ b/src/basic/sort-util.h @@ -39,7 +39,7 @@ static inline void* bsearch_safe(const void *key, const void *base, * Normal qsort requires base to be nonnull. Here were require * that only if nmemb > 0. */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { +static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) { if (nmemb <= 1) return; @@ -52,7 +52,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn #define typesafe_qsort(p, n, func) \ ({ \ int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ }) static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) { diff --git a/src/basic/special.h b/src/basic/special.h index 0eb3f3a36..19ee30cd4 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -82,6 +82,7 @@ #define SPECIAL_QUOTAON_SERVICE "quotaon.service" #define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" #define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service" +#define SPECIAL_UDEVD_SERVICE "systemd-udevd.service" /* Services systemd relies on */ #define SPECIAL_DBUS_SERVICE "dbus.service" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 4b3d84034..904584a98 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "missing_fs.h" @@ -77,10 +78,9 @@ int dir_is_empty_at(int dir_fd, const char *path) { if (fd < 0) return -errno; - d = fdopendir(fd); + d = take_fdopendir(&fd); if (!d) return -errno; - fd = -1; FOREACH_DIRENT(de, d, return -errno) return 0; @@ -380,3 +380,36 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret return 0; } + +int proc_mounted(void) { + int r; + + /* A quick check of procfs is properly mounted */ + + r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC); + if (r == -ENOENT) /* not mounted at all */ + return false; + + return r; +} + +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 + * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file + * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file + * size, backing device, inode type and if this refers to a device not the major/minor. + * + * Note that we don't care if file attributes such as ownership or access mode change, this here is + * 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 */ + a->st_mtime == b->st_mtime && + (!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 7824af350..59aedcb7c 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -87,3 +87,7 @@ int fd_verify_directory(int fd); int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret); int device_path_make_canonical(mode_t mode, dev_t devno, char **ret); int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno); + +int proc_mounted(void); + +bool stat_inode_unmodified(const struct stat *a, const struct stat *b); diff --git a/src/basic/string-table.c b/src/basic/string-table.c index 34931b03d..0168cff88 100644 --- a/src/basic/string-table.c +++ b/src/basic/string-table.c @@ -4,12 +4,10 @@ #include "string-util.h" ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { - size_t i; - if (!key) return -1; - for (i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) if (streq_ptr(table[i], key)) return (ssize_t) i; diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 93b7a2203..755a37f66 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -19,18 +19,19 @@ #include "util.h" int strcmp_ptr(const char *a, const char *b) { - /* Like strcmp(), but tries to make sense of NULL pointers */ + if (a && b) return strcmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ +} - if (!a && b) - return -1; +int strcasecmp_ptr(const char *a, const char *b) { + /* Like strcasecmp(), but tries to make sense of NULL pointers */ - if (a && !b) - return 1; - - return 0; + if (a && b) + return strcasecmp(a, b); + return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ } char* endswith(const char *s, const char *postfix) { @@ -126,45 +127,58 @@ static size_t strcspn_escaped(const char *s, const char *reject) { } /* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { +const char* split( + const char **state, + size_t *l, + const char *separator, + SplitFlags flags) { + const char *current; + assert(state); + assert(l); + + if (!separator) + separator = WHITESPACE; + current = *state; - if (!*current) { - assert(**state == '\0'); + if (*current == '\0') /* already at the end? */ return NULL; - } - current += strspn(current, separator); - if (!*current) { + current += strspn(current, separator); /* skip leading separators */ + if (*current == '\0') { /* at the end now? */ *state = current; return NULL; } - if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) { - char quotechars[2] = {*current, '\0'}; + if (FLAGS_SET(flags, SPLIT_QUOTES)) { - *l = strcspn_escaped(current + 1, quotechars); - if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || - (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - if (flags & SPLIT_RELAX) { - *state = current + *l + 1 + (current[*l + 1] != '\0'); - return current + 1; + if (strchr(QUOTES, *current)) { + /* We are looking at a quote */ + *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current)); + if (current[*l + 1] != *current || + (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) { + /* right quote missing or garbage at the end */ + if (FLAGS_SET(flags, SPLIT_RELAX)) { + *state = current + *l + 1 + (current[*l + 1] != '\0'); + return current + 1; + } + *state = current; + return NULL; } - *state = current; - return NULL; + *state = current++ + *l + 2; + + } else { + /* We are looking at a something that is not a quote */ + *l = strcspn_escaped(current, separator); + if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) { + /* unfinished escape */ + *state = current; + return NULL; + } + *state = current + *l; } - *state = current++ + *l + 2; - } else if (flags & SPLIT_QUOTES) { - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) { - /* unfinished escape */ - *state = current; - return NULL; - } - *state = current + *l; } else { *l = strcspn(current, separator); *state = current + *l; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index f98fbdddd..09131455b 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -27,6 +27,7 @@ #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) int strcmp_ptr(const char *a, const char *b) _pure_; +int strcasecmp_ptr(const char *a, const char *b) _pure_; static inline bool streq_ptr(const char *a, const char *b) { return strcmp_ptr(a, b) == 0; @@ -112,8 +113,10 @@ typedef enum SplitFlags { SPLIT_RELAX = 0x01 << 1, } SplitFlags; +/* Smelly. Do not use this anymore. Use extract_first_word() instead! */ const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); +/* Similar, don't use this anymore */ #define FOREACH_WORD(word, length, s, state) \ _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) diff --git a/src/basic/strv.c b/src/basic/strv.c index 096cb4e5d..858e1e62e 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -28,6 +28,18 @@ char *strv_find(char * const *l, const char *name) { return NULL; } +char *strv_find_case(char * const *l, const char *name) { + char * const *i; + + assert(name); + + STRV_FOREACH(i, l) + if (strcaseeq(*i, name)) + return *i; + + return NULL; +} + char *strv_find_prefix(char * const *l, const char *name) { char * const *i; @@ -934,20 +946,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c return 1; } -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) { +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; return string_strv_hashmap_put_internal(*h, key, value); } -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) { +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) { int r; - r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops); + r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS); if (r < 0) return r; diff --git a/src/basic/strv.h b/src/basic/strv.h index 637a98121..2ad927bce 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -14,9 +14,13 @@ #include "string-util.h" char *strv_find(char * const *l, const char *name) _pure_; +char *strv_find_case(char * const *l, const char *name) _pure_; char *strv_find_prefix(char * const *l, const char *name) _pure_; char *strv_find_startswith(char * const *l, const char *name) _pure_; +#define strv_contains(l, s) (!!strv_find((l), (s))) +#define strv_contains_case(l, s) (!!strv_find_case((l), (s))) + char **strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) @@ -54,8 +58,6 @@ static inline bool strv_equal(char * const *a, char * const *b) { return strv_compare(a, b) == 0; } -#define strv_contains(l, s) (!!strv_find((l), (s))) - char **strv_new_internal(const char *x, ...) _sentinel_; char **strv_new_ap(const char *x, va_list ap); #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) @@ -87,6 +89,16 @@ char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); int strv_make_nulstr(char * const *l, char **p, size_t *n); +static inline int strv_from_nulstr(char ***a, const char *nulstr) { + char **t; + + t = strv_split_nulstr(nulstr); + if (!t) + return -ENOMEM; + *a = t; + return 0; +} + bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH(s, l) \ @@ -94,7 +106,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; #define STRV_FOREACH_BACKWARDS(s, l) \ for (s = ({ \ - char **_l = l; \ + typeof(l) _l = l; \ _l ? _l + strv_length(_l) - 1U : NULL; \ }); \ (l) && ((s) >= (l)); \ @@ -146,6 +158,13 @@ void strv_print(char * const *l); _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ }) +#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x) +#define STRCASEPTR_IN_SET(x, ...) \ + ({ \ + const char* _x = (x); \ + _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \ + }) + #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ @@ -207,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); }) extern const struct hash_ops string_strv_hash_ops; -int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value); -int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value); +int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS); +#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) +#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS) diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index efc22b159..67634b4d4 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -28,9 +28,10 @@ #define ANSI_HIGHLIGHT_YELLOW4 "\x1B[0;1;38;5;100m" /* Underlined */ +#define ANSI_GREY_UNDERLINE "\x1B[0;4;38;5;245m" #define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m" #define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m" -#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m" +#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;38;5;185m" #define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m" #define ANSI_HIGHLIGHT_MAGENTA_UNDERLINE "\x1B[0;1;4;35m" #define ANSI_HIGHLIGHT_GREY_UNDERLINE "\x1B[0;1;4;38;5;245m" @@ -138,6 +139,7 @@ DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY); DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL); DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT); +DEFINE_ANSI_FUNC_UNDERLINE(grey_underline, GREY_UNDERLINE, GREY); DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED); DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN); DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 105584e2e..15cc1b885 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -82,43 +82,82 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { return ts; } +static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) { + + /* Maps the time 'from' between two clocks, based on a common reference point where the first clock + * is at 'from_base' and the second clock at 'to_base'. Basically calculates: + * + * from - from_base + to_base + * + * But takes care of overflows/underflows and avoids signed operations. */ + + if (from >= from_base) { /* In the future */ + usec_t delta = from - from_base; + + if (to_base >= USEC_INFINITY - delta) /* overflow? */ + return USEC_INFINITY; + + return to_base + delta; + + } else { /* In the past */ + usec_t delta = from_base - from; + + if (to_base <= delta) /* underflow? */ + return 0; + + return to_base - delta; + } +} + +usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { + + /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock + * onto itself */ + if (map_clock_id(from_clock) == map_clock_id(to_clock)) + return from; + + /* Keep infinity as is */ + if (from == USEC_INFINITY) + return from; + + return map_clock_usec_internal(from, now(from_clock), now(to_clock)); +} + dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; assert(ts); - if (u == USEC_INFINITY || u <= 0) { + if (u == USEC_INFINITY || u == 0) { ts->realtime = ts->monotonic = u; return ts; } ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - + ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC); return ts; } triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; + usec_t nowr; assert(ts); - if (u == USEC_INFINITY || u <= 0) { + if (u == USEC_INFINITY || u == 0) { ts->realtime = ts->monotonic = ts->boottime = u; return ts; } + nowr = now(CLOCK_REALTIME); + ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; + 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; return ts; } dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; assert(ts); if (u == USEC_INFINITY) { @@ -127,25 +166,28 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { } ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); - + ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME); return ts; } dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; + clockid_t cid; + usec_t nowm; if (u == USEC_INFINITY) { ts->realtime = ts->monotonic = USEC_INFINITY; return ts; } - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub_signed(ts->realtime, delta); - ts->monotonic = usec_sub_signed(ts->monotonic, delta); + 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)); + + ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); return ts; } diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 4c371257e..9bbe98630 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -29,8 +29,8 @@ typedef struct triple_timestamp { usec_t boottime; } triple_timestamp; -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) +#define USEC_INFINITY ((usec_t) UINT64_MAX) +#define NSEC_INFINITY ((nsec_t) UINT64_MAX) #define MSEC_PER_SEC 1000ULL #define USEC_PER_SEC ((usec_t) 1000000ULL) @@ -67,6 +67,8 @@ typedef struct triple_timestamp { usec_t now(clockid_t clock); nsec_t now_nsec(clockid_t clock); +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); diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index decdafb9c..a49f7eee7 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -48,14 +48,12 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { /* This assumes that returned FILE object is short-lived and used within the same single-threaded * context and never shared externally, hence locking is not necessary. */ - r = fdopen_unlocked(fd, "w", &f); + r = take_fdopen_unlocked(&fd, "w", &f); if (r < 0) { (void) unlink(t); return r; } - TAKE_FD(fd); - if (ret_f) *ret_f = TAKE_PTR(f); @@ -80,18 +78,16 @@ int mkostemp_safe(char *pattern) { } int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { - int fd; + _cleanup_close_ int fd = -1; FILE *f; fd = mkostemp_safe(pattern); if (fd < 0) return fd; - f = fdopen(fd, mode); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, mode); + if (!f) return -errno; - } *ret_f = f; return 0; @@ -261,7 +257,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { assert((flags & O_EXCL) == 0); /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in + * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ fd = open_parent(target, O_TMPFILE|flags, 0640); diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index dba218b38..94ec1f3d1 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -108,6 +108,15 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); +static const char* const freezer_state_table[_FREEZER_STATE_MAX] = { + [FREEZER_RUNNING] = "running", + [FREEZER_FREEZING] = "freezing", + [FREEZER_FROZEN] = "frozen", + [FREEZER_THAWING] = "thawing", +}; + +DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState); + static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { [AUTOMOUNT_DEAD] = "dead", [AUTOMOUNT_WAITING] = "waiting", @@ -176,6 +185,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = { [SERVICE_STOP_SIGTERM] = "stop-sigterm", [SERVICE_STOP_SIGKILL] = "stop-sigkill", [SERVICE_STOP_POST] = "stop-post", + [SERVICE_FINAL_WATCHDOG] = "final-watchdog", [SERVICE_FINAL_SIGTERM] = "final-sigterm", [SERVICE_FINAL_SIGKILL] = "final-sigkill", [SERVICE_FAILED] = "failed", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index 5979819dc..53419ecd8 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -7,7 +7,7 @@ /* The enum order is used to order unit jobs in the job queue * when other criteria (cpu weight, nice level) are identical. - * In this case service units have the hightest priority. */ + * In this case service units have the highest priority. */ typedef enum UnitType { UNIT_SERVICE = 0, UNIT_MOUNT, @@ -48,6 +48,15 @@ typedef enum UnitActiveState { _UNIT_ACTIVE_STATE_INVALID = -1 } UnitActiveState; +typedef enum FreezerState { + FREEZER_RUNNING, + FREEZER_FREEZING, + FREEZER_FROZEN, + FREEZER_THAWING, + _FREEZER_STATE_MAX, + _FREEZER_STATE_INVALID = -1 +} FreezerState; + typedef enum AutomountState { AUTOMOUNT_DEAD, AUTOMOUNT_WAITING, @@ -118,6 +127,7 @@ typedef enum ServiceState { SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_WATCHDOG, /* In case the STOP_POST executable needs to be aborted. */ SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */ SERVICE_FINAL_SIGKILL, SERVICE_FAILED, @@ -253,6 +263,9 @@ UnitLoadState unit_load_state_from_string(const char *s) _pure_; const char *unit_active_state_to_string(UnitActiveState i) _const_; UnitActiveState unit_active_state_from_string(const char *s) _pure_; +const char *freezer_state_to_string(FreezerState i) _const_; +FreezerState freezer_state_from_string(const char *s) _pure_; + const char* automount_state_to_string(AutomountState i) _const_; AutomountState automount_state_from_string(const char *s) _pure_; diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 521364124..43d8b3477 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -207,8 +207,9 @@ UnitType unit_name_to_type(const char *n) { } int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { - char *e, *s; + _cleanup_free_ char *s = NULL; size_t a, b; + char *e; assert(n); assert(suffix); @@ -230,8 +231,12 @@ int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { return -ENOMEM; strcpy(mempcpy(s, n, a), suffix); - *ret = s; + /* Make sure the name is still valid (i.e. didn't grow too large due to longer suffix) */ + if (!unit_name_is_valid(s, UNIT_NAME_ANY)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 0; } @@ -253,8 +258,8 @@ int unit_name_build(const char *prefix, const char *instance, const char *suffix } int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) { + _cleanup_free_ char *s = NULL; const char *ut; - char *s; assert(prefix); assert(type >= 0); @@ -264,19 +269,23 @@ int unit_name_build_from_type(const char *prefix, const char *instance, UnitType if (!unit_prefix_is_valid(prefix)) return -EINVAL; - if (instance && !unit_instance_is_valid(instance)) - return -EINVAL; - ut = unit_type_to_string(type); - if (!instance) - s = strjoin(prefix, ".", ut); - else + if (instance) { + if (!unit_instance_is_valid(instance)) + return -EINVAL; + s = strjoin(prefix, "@", instance, ".", ut); + } else + s = strjoin(prefix, ".", ut); if (!s) return -ENOMEM; - *ret = s; + /* Verify that this didn't grow too large (or otherwise is invalid) */ + if (!unit_name_is_valid(s, instance ? UNIT_NAME_INSTANCE : UNIT_NAME_PLAIN)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 0; } @@ -441,8 +450,8 @@ int unit_name_path_unescape(const char *f, char **ret) { } int unit_name_replace_instance(const char *f, const char *i, char **ret) { + _cleanup_free_ char *s = NULL; const char *p, *e; - char *s; size_t a, b; assert(f); @@ -466,7 +475,11 @@ int unit_name_replace_instance(const char *f, const char *i, char **ret) { strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); - *ret = s; + /* Make sure the resulting name still is valid, i.e. didn't grow too large */ + if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 0; } @@ -497,8 +510,7 @@ int unit_name_template(const char *f, char **ret) { } int unit_name_from_path(const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s = NULL; + _cleanup_free_ char *p = NULL, *s = NULL; int r; assert(path); @@ -516,13 +528,16 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) { if (!s) return -ENOMEM; - *ret = s; + /* Refuse this if this got too long or for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 0; } int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s; + _cleanup_free_ char *p = NULL, *s = NULL; int r; assert(prefix); @@ -544,7 +559,11 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha if (!s) return -ENOMEM; - *ret = s; + /* Refuse this if this got too long or for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 0; } @@ -597,9 +616,9 @@ static bool do_escape_mangle(const char *f, bool allow_globs, char *t) { * If @allow_globs, globs characters are preserved. Otherwise, they are escaped. */ int unit_name_mangle_with_suffix(const char *name, const char *operation, UnitNameMangle flags, const char *suffix, char **ret) { - char *s; - int r; + _cleanup_free_ char *s = NULL; bool mangled, suggest_escape = true; + int r; assert(name); assert(suffix); @@ -657,7 +676,12 @@ int unit_name_mangle_with_suffix(const char *name, const char *operation, UnitNa if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0) strcat(s, suffix); - *ret = s; + /* Make sure mangling didn't grow this too large (but don't do this check if globbing is allowed, + * since globs generally do not qualify as valid unit names) */ + if (!FLAGS_SET(flags, UNIT_NAME_MANGLE_GLOB) && !unit_name_is_valid(s, UNIT_NAME_ANY)) + return -EINVAL; + + *ret = TAKE_PTR(s); return 1; good: @@ -665,12 +689,13 @@ good: if (!s) return -ENOMEM; - *ret = s; + *ret = TAKE_PTR(s); return 0; } int slice_build_parent_slice(const char *slice, char **ret) { - char *s, *dash; + _cleanup_free_ char *s = NULL; + char *dash; int r; assert(slice); @@ -693,13 +718,11 @@ int slice_build_parent_slice(const char *slice, char **ret) { strcpy(dash, ".slice"); else { r = free_and_strdup(&s, SPECIAL_ROOT_SLICE); - if (r < 0) { - free(s); + if (r < 0) return r; - } } - *ret = s; + *ret = TAKE_PTR(s); return 1; } diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 7dd2f6664..8115065b5 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -290,7 +290,7 @@ int get_user_creds( (empty_or_root(p->pw_dir) || !path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir))) - *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */ + *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */ else *home = p->pw_dir; } @@ -777,7 +777,7 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags) { return false; if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused - * with with UIDs (note that this test is more broad than + * with UIDs (note that this test is more broad than * the parse_uid() test above, as it will cover more than * the 32bit range, and it will detect 65535 (which is in * invalid UID, even though in the unsigned 32 bit range) */ diff --git a/src/basic/virt.c b/src/basic/virt.c index f56769626..c6bff6b16 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -441,6 +441,7 @@ static const char *const container_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_PODMAN] = "podman", [VIRTUALIZATION_RKT] = "rkt", [VIRTUALIZATION_WSL] = "wsl", + [VIRTUALIZATION_PROOT] = "proot", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int); @@ -449,6 +450,7 @@ int detect_container(void) { static thread_local int cached_found = _VIRTUALIZATION_INVALID; _cleanup_free_ char *m = NULL; _cleanup_free_ char *o = NULL; + _cleanup_free_ char *p = NULL; const char *e = NULL; int r; @@ -462,16 +464,32 @@ int detect_container(void) { goto finish; } - /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364, - * ... and a working one, since the official one doesn't actually work ;( - */ + /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */ r = read_one_line_file("/proc/sys/kernel/osrelease", &o); if (r >= 0 && - (strstr(o, "Microsoft") || strstr(o, "microsoft") || strstr(o, "WSL"))) { + (strstr(o, "Microsoft") || strstr(o, "WSL"))) { r = VIRTUALIZATION_WSL; goto finish; } + /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this + * invocation without worrying about it being elsewhere. + */ + r = get_proc_field("/proc/self/status", "TracerPid", WHITESPACE, &p); + if (r == 0 && !streq(p, "0")) { + pid_t ptrace_pid; + r = parse_pid(p, &ptrace_pid); + if (r == 0) { + const char *pf = procfs_file_alloca(ptrace_pid, "comm"); + _cleanup_free_ char *ptrace_comm = NULL; + r = read_one_line_file(pf, &ptrace_comm); + if (r >= 0 && startswith(ptrace_comm, "proot")) { + r = VIRTUALIZATION_PROOT; + goto finish; + } + } + } + if (getpid_cached() == 1) { /* If we are PID 1 we can just check our own environment variable, and that's authoritative. * We distinguish three cases: @@ -660,6 +678,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_PODMAN] = "podman", [VIRTUALIZATION_RKT] = "rkt", [VIRTUALIZATION_WSL] = "wsl", + [VIRTUALIZATION_PROOT] = "proot", [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", }; diff --git a/src/basic/virt.h b/src/basic/virt.h index 26f409afd..d58c582c9 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -34,6 +34,7 @@ enum { VIRTUALIZATION_PODMAN, VIRTUALIZATION_RKT, VIRTUALIZATION_WSL, + VIRTUALIZATION_PROOT, VIRTUALIZATION_CONTAINER_OTHER, VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 352502183..a663fc5c2 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -982,8 +982,9 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) { char machine_string[SD_ID128_STRING_MAX]; _cleanup_(unlink_and_freep) char *t = NULL; _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; const char *p; - int r, fd; + int r; p = prefix_roota(esp_path, "/loader/loader.conf"); if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ @@ -993,11 +994,9 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) { if (fd < 0) return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p); - f = fdopen(fd, "w"); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, "w"); + if (!f) return log_oom(); - } fprintf(f, "#timeout 3\n" "#console-mode keep\n" @@ -1007,7 +1006,7 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) { if (r < 0) return log_error_errno(r, "Failed to write \"%s\": %m", p); - r = link_tmpfile(fd, t, p); + r = link_tmpfile(fileno(f), t, p); if (r == -EEXIST) return 0; /* Silently skip creation if the file exists now (recheck) */ if (r < 0) @@ -1042,7 +1041,10 @@ static int help(int argc, char *argv[], void *userdata) { " remove Remove systemd-boot from the ESP and EFI variables\n" " is-installed Test whether systemd-boot is installed in the ESP\n" " random-seed Initialize random seed in ESP and EFI variables\n" - " systemd-efi-options Query or set system options string in EFI variable\n" + " systemd-efi-options [STRING]\n" + " Query or set system options string in EFI variable\n" + " reboot-to-firmware [BOOL]\n" + " Query or set reboot-to-firmware EFI flag\n" "\nBoot Loader Entries Commands:\n" " list List boot loader entries\n" " set-default ID Set default boot loader entry\n" @@ -1246,6 +1248,18 @@ static int verb_status(int argc, char *argv[], void *userdata) { printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal()); printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot())); printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user"); + + r = efi_get_reboot_to_firmware(); + if (r > 0) + printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal()); + else if (r == 0) + printf(" Boot into FW: supported\n"); + else if (r == -EOPNOTSUPP) + printf(" Boot into FW: not supported\n"); + else { + errno = -r; + printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal()); + } printf("\n"); printf("Current Boot Loader:\n"); @@ -1312,6 +1326,7 @@ 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_strv_free_ char **efi_entries = NULL; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn @@ -1334,7 +1349,13 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (r < 0) return r; - (void) boot_entries_augment_from_loader(&config, false); + r = efi_loader_get_entries(&efi_entries); + if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r)) + log_debug_errno(r, "Boot loader reported no entries."); + 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); if (config.n_entries == 0) log_info("No boot loader entries found."); @@ -1436,7 +1457,7 @@ static int install_random_seed(const char *esp) { * the EFI variable space we can make sure that even though the random seeds on disk * are all the same they will be different on each system under the assumption that * the EFI variable space is maintained separate from the random seed storage. That - * is generally the case on physical systems, as the ESP is stored on persistant + * is generally the case on physical systems, as the ESP is stored on persistent * storage, and the EFI variables in NVRAM. However in virtualized environments this * is generally not true: the EFI variable set is typically stored along with the * disk image itself. For example, using the OVMF EFI firmware the EFI variables are @@ -1767,6 +1788,39 @@ static int verb_systemd_efi_options(int argc, char *argv[], void *userdata) { return 0; } +static int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) { + int r; + + if (argc < 2) { + r = efi_get_reboot_to_firmware(); + if (r > 0) { + puts("active"); + return EXIT_SUCCESS; /* success */ + } + if (r == 0) { + puts("supported"); + return 1; /* recognizable error #1 */ + } + if (r == -EOPNOTSUPP) { + puts("not supported"); + return 2; /* recognizable error #2 */ + } + + log_error_errno(r, "Failed to query reboot-to-firmware state: %m"); + return 3; /* other kind of error */ + } else { + r = parse_boolean(argv[1]); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %s", argv[1]); + + r = efi_set_reboot_to_firmware(r); + if (r < 0) + return log_error_errno(r, "Failed to set reboot-to-firmware option: %m"); + + return 0; + } +} + static int bootctl_main(int argc, char *argv[]) { static const Verb verbs[] = { { "help", VERB_ANY, VERB_ANY, 0, help }, @@ -1780,6 +1834,7 @@ static int bootctl_main(int argc, char *argv[]) { { "set-oneshot", 2, 2, 0, verb_set_default }, { "random-seed", VERB_ANY, 1, 0, verb_random_seed }, { "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options }, + { "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware }, {} }; @@ -1803,4 +1858,4 @@ static int run(int argc, char *argv[]) { return bootctl_main(argc, argv); } -DEFINE_MAIN_FUNCTION(run); +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 99938c547..5189d86d1 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -272,6 +272,8 @@ static BOOLEAN line_edit( case KEYPRESS(0, 0, CHAR_LINEFEED): case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): + case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */ + 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; @@ -742,6 +744,9 @@ static BOOLEAN menu_run( case KEYPRESS(0, 0, CHAR_LINEFEED): case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): + case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */ + case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */ + case KEYPRESS(0, SCAN_RIGHT, 0): exit = TRUE; break; @@ -1200,7 +1205,7 @@ static VOID config_entry_parse_tries( } new_factor = factor * 10; - if (new_factor < factor) /* overflow chck */ + if (new_factor < factor) /* overflow check */ return; factor = new_factor; @@ -1518,11 +1523,11 @@ static VOID config_load_entries( static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) { INTN r; - /* Order entries that have no tries left to the end of the list */ + /* 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; + if (a->tries_left == 0 && b->tries_left != 0) + return -1; r = str_verscmp(a->id, b->id); if (r != 0) @@ -1532,17 +1537,17 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) { b->tries_left == (UINTN) -1) return 0; - /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */ + /* 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 (a->tries_left < b->tries_left) + return -1; /* 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; + if (a->tries_done > b->tries_done) + return -1; return 0; } diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 768cb51d0..0b712d856 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -302,9 +302,8 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN EFI_TCG2 *tpm2; tpm2 = tcg2_interface_check(); - if (tpm2) { + if (tpm2) return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); - } tpm1 = tcg1_interface_check(); if (tpm1) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 3c75be381..ccb22d5f8 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -101,7 +101,8 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) { r = sd_bus_set_watch_bind(bus, arg_watch_bind); if (r < 0) - return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind)); + return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", + yes_no(arg_watch_bind)); if (arg_address) r = sd_bus_set_address(bus, arg_address); @@ -109,13 +110,10 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) { switch (arg_transport) { case BUS_TRANSPORT_LOCAL: - if (arg_user) { - bus->is_user = true; + if (arg_user) r = bus_set_address_user(bus); - } else { - bus->is_system = true; + else r = bus_set_address_system(bus); - } break; case BUS_TRANSPORT_REMOTE: @@ -172,7 +170,9 @@ static int list_bus_names(int argc, char **argv, void *userdata) { if (r < 0) return r; - r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); + r = sd_bus_list_names(bus, + (arg_acquired || arg_unique) ? &acquired : NULL, + arg_activatable ? &activatable : NULL); if (r < 0) return log_error_errno(r, "Failed to list names: %m"); @@ -192,7 +192,16 @@ static int list_bus_names(int argc, char **argv, void *userdata) { return log_error_errno(r, "Failed to add to hashmap: %m"); } - table = table_new("activatable", "name", "pid", "process", "user", "connection", "unit", "session", "description", "machine"); + table = table_new("activatable", + "name", + "pid", + "process", + "user", + "connection", + "unit", + "session", + "description", + "machine"); if (!table) return log_oom(); @@ -354,15 +363,15 @@ static int list_bus_names(int argc, char **argv, void *userdata) { return log_error_errno(r, "Failed to fill line: %m"); } - if (IN_SET(arg_json, JSON_OFF, JSON_PRETTY)) - (void) pager_open(arg_pager_flags); + (void) pager_open(arg_pager_flags); if (arg_json) - r = table_print_json(table, stdout, (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) | JSON_FORMAT_COLOR_AUTO); + r = table_print_json(table, stdout, + (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) | JSON_FORMAT_COLOR_AUTO); else r = table_print(table, stdout); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); return 0; } @@ -405,36 +414,25 @@ static void print_subtree(const char *prefix, const char *path, char **l) { n++; } - printf("%s%s%s\n", prefix, special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), *l); + printf("%s%s%s\n", + prefix, + special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), + *l); print_subtree(has_more ? vertical : space, *l, l); l = n; } } -static void print_tree(const char *prefix, char **l) { - - prefix = strempty(prefix); - - if (arg_list) { - char **i; - - STRV_FOREACH(i, l) - printf("%s%s\n", prefix, *i); - return; - } - - if (strv_isempty(l)) { +static void print_tree(char **l) { + if (arg_list) + strv_print(l); + else if (strv_isempty(l)) printf("No objects discovered.\n"); - return; - } - - if (streq(l[0], "/") && !l[1]) { + else if (streq(l[0], "/") && !l[1]) printf("Only root object discovered.\n"); - return; - } - - print_subtree(prefix, "/", l); + else + print_subtree("", "/", l); } static int on_path(const char *path, void *userdata) { @@ -443,14 +441,14 @@ static int on_path(const char *path, void *userdata) { assert(paths); - r = set_put_strdup(paths, path); + r = set_put_strdup(&paths, path); if (r < 0) return log_oom(); return 0; } -static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) { +static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths) { static const XMLIntrospectOps ops = { .on_path = on_path, }; @@ -460,12 +458,14 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p const char *xml; int r; - r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + r = sd_bus_call_method(bus, service, path, + "org.freedesktop.DBus.Introspectable", "Introspect", + &error, &reply, ""); if (r < 0) { - if (many) - printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r)); - else - log_error_errno(r, "Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); + printf("%sFailed to introspect object %s of service %s: %s%s\n", + ansi_highlight_red(), + path, service, bus_error_message(&error, r), + ansi_normal()); return r; } @@ -476,34 +476,23 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p return parse_xml_introspect(path, xml, &ops, paths); } -static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) { - _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL; +static int tree_one(sd_bus *bus, const char *service) { + _cleanup_set_free_ Set *paths = NULL, *done = NULL, *failed = NULL; _cleanup_free_ char **l = NULL; - char *m; int r; - paths = set_new(&string_hash_ops); - if (!paths) + r = set_put_strdup(&paths, "/"); + if (r < 0) return log_oom(); - done = set_new(&string_hash_ops); + done = set_new(&string_hash_ops_free); if (!done) return log_oom(); - failed = set_new(&string_hash_ops); + failed = set_new(&string_hash_ops_free); if (!failed) return log_oom(); - m = strdup("/"); - if (!m) - return log_oom(); - - r = set_put(paths, m); - if (r < 0) { - free(m); - return log_oom(); - } - for (;;) { _cleanup_free_ char *p = NULL; int q; @@ -516,20 +505,14 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m set_contains(failed, p)) continue; - q = find_nodes(bus, service, p, paths, many); - if (q < 0) { - if (r >= 0) - r = q; - - q = set_put(failed, p); - } else - q = set_put(done, p); + q = find_nodes(bus, service, p, paths); + if (q < 0 && r >= 0) + r = q; + q = set_consume(q < 0 ? failed : done, TAKE_PTR(p)); + assert(q != 0); if (q < 0) return log_oom(); - - assert(q != 0); - p = NULL; } (void) pager_open(arg_pager_flags); @@ -539,7 +522,7 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m return log_oom(); strv_sort(l); - print_tree(prefix, l); + print_tree(l); fflush(stdout); @@ -551,6 +534,12 @@ static int tree(int argc, char **argv, void *userdata) { char **i; int r = 0; + /* Do superficial verification of arguments before even opening the bus */ + STRV_FOREACH(i, strv_skip(argv, 1)) + if (!sd_bus_service_name_is_valid(*i)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid bus service name: %s", *i); + if (!arg_unique && !arg_acquired) arg_acquired = true; @@ -582,14 +571,14 @@ static int tree(int argc, char **argv, void *userdata) { printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); - q = tree_one(bus, *i, NULL, true); + q = tree_one(bus, *i); if (q < 0 && r >= 0) r = q; not_first = true; } - } else { - STRV_FOREACH(i, argv+1) { + } else + STRV_FOREACH(i, strv_skip(argv, 1)) { int q; if (i > argv+1) @@ -600,11 +589,10 @@ static int tree(int argc, char **argv, void *userdata) { printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); } - q = tree_one(bus, *i, NULL, !!argv[2]); + q = tree_one(bus, *i); if (q < 0 && r >= 0) r = q; } - } return r; } @@ -837,20 +825,24 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) { assert(interface); assert(members); - m = new0(Member, 1); + m = new(Member, 1); if (!m) return log_oom(); - m->type = "interface"; - m->flags = flags; + *m = (Member) { + .type = "interface", + .flags = flags, + }; r = free_and_strdup(&m->interface, interface); if (r < 0) return log_oom(); r = set_put(members, m); - if (r <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate interface"); + if (r == -EEXIST) + return log_error_errno(r, "Invalid introspection data: duplicate interface '%s'.", interface); + if (r < 0) + return log_oom(); m = NULL; return 0; @@ -864,12 +856,14 @@ static int on_method(const char *interface, const char *name, const char *signat assert(interface); assert(name); - m = new0(Member, 1); + m = new(Member, 1); if (!m) return log_oom(); - m->type = "method"; - m->flags = flags; + *m = (Member) { + .type = "method", + .flags = flags, + }; r = free_and_strdup(&m->interface, interface); if (r < 0) @@ -888,8 +882,10 @@ static int on_method(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate method"); + if (r == -EEXIST) + return log_error_errno(r, "Invalid introspection data: duplicate method '%s' on interface '%s'.", name, interface); + if (r < 0) + return log_oom(); m = NULL; return 0; @@ -903,12 +899,14 @@ static int on_signal(const char *interface, const char *name, const char *signat assert(interface); assert(name); - m = new0(Member, 1); + m = new(Member, 1); if (!m) return log_oom(); - m->type = "signal"; - m->flags = flags; + *m = (Member) { + .type = "signal", + .flags = flags, + }; r = free_and_strdup(&m->interface, interface); if (r < 0) @@ -923,8 +921,10 @@ static int on_signal(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate signal"); + if (r == -EEXIST) + return log_error_errno(r, "Invalid introspection data: duplicate signal '%s' on interface '%s'.", name, interface); + if (r < 0) + return log_oom(); m = NULL; return 0; @@ -938,13 +938,15 @@ static int on_property(const char *interface, const char *name, const char *sign assert(interface); assert(name); - m = new0(Member, 1); + m = new(Member, 1); if (!m) return log_oom(); - m->type = "property"; - m->flags = flags; - m->writable = writable; + *m = (Member) { + .type = "property", + .flags = flags, + .writable = writable, + }; r = free_and_strdup(&m->interface, interface); if (r < 0) @@ -959,8 +961,10 @@ static int on_property(const char *interface, const char *name, const char *sign return log_oom(); r = set_put(members, m); - if (r <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate property"); + if (r == -EEXIST) + return log_error_errno(r, "Invalid introspection data: duplicate property '%s' on interface '%s'.", name, interface); + if (r < 0) + return log_oom(); m = NULL; return 0; @@ -994,9 +998,12 @@ static int introspect(int argc, char **argv, void *userdata) { if (!members) return log_oom(); - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, ""); + r = sd_bus_call_method(bus, argv[1], argv[2], + "org.freedesktop.DBus.Introspectable", "Introspect", + &error, &reply_xml, ""); if (r < 0) - return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r)); + return log_error_errno(r, "Failed to introspect object %s of service %s: %s", + argv[2], argv[1], bus_error_message(&error, r)); r = sd_bus_message_read(reply_xml, "s", &xml); if (r < 0) @@ -1004,6 +1011,7 @@ static int introspect(int argc, char **argv, void *userdata) { if (arg_xml_interface) { /* Just dump the received XML and finish */ + (void) pager_open(arg_pager_flags); puts(xml); return 0; } @@ -1026,7 +1034,9 @@ static int introspect(int argc, char **argv, void *userdata) { if (argv[3] && !streq(argv[3], m->interface)) continue; - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface); + r = sd_bus_call_method(bus, argv[1], argv[2], + "org.freedesktop.DBus.Properties", "GetAll", + &error, &reply, "s", m->interface); if (r < 0) return log_error_errno(r, "Failed to get all properties on interface %s: %s", m->interface, bus_error_message(&error, r)); @@ -1088,17 +1098,14 @@ static int introspect(int argc, char **argv, void *userdata) { return bus_log_parse_error(r); } - (void) pager_open(arg_pager_flags); - - name_width = STRLEN("NAME"); - type_width = STRLEN("TYPE"); - signature_width = STRLEN("SIGNATURE"); - result_width = STRLEN("RESULT/VALUE"); + name_width = strlen("NAME"); + type_width = strlen("TYPE"); + signature_width = strlen("SIGNATURE"); + result_width = strlen("RESULT/VALUE"); sorted = newa(Member*, set_size(members)); SET_FOREACH(m, members, i) { - if (argv[3] && !streq(argv[3], m->interface)) continue; @@ -1123,6 +1130,8 @@ static int introspect(int argc, char **argv, void *userdata) { typesafe_qsort(sorted, k, member_compare_funcp); + (void) pager_open(arg_pager_flags); + if (arg_legend) { printf("%-*s %-*s %-*s %-*s %s\n", (int) name_width, "NAME", @@ -1159,7 +1168,8 @@ static int introspect(int argc, char **argv, void *userdata) { printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", is_interface ? ansi_highlight() : "", is_interface ? "" : ".", - - !is_interface + (int) name_width, empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name), + - !is_interface + (int) name_width, + empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name), is_interface ? ansi_normal() : "", (int) type_width, empty_to_dash(m->type), (int) signature_width, empty_to_dash(m->signature), @@ -1196,23 +1206,22 @@ static int message_json(sd_bus_message *m, FILE *f) { e[1] = 0; r = json_build(&w, JSON_BUILD_OBJECT( - JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))), - JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)), - JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)), - JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)), - JSON_BUILD_PAIR_CONDITION(m->priority != 0, "priority", JSON_BUILD_INTEGER(m->priority)), - JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))), - JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)), - JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)), - JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)), - JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)), - JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)), - JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)), - JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)), - JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)), - JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)), - JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)), - JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v)))); + JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))), + JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)), + JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)), + JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)), + JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))), + JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)), + JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)), + JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)), + JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)), + JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)), + JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)), + JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)), + JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)), + JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)), + JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)), + JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v)))); if (r < 0) return log_error_errno(r, "Failed to build JSON object: %m"); @@ -1251,7 +1260,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f STRV_FOREACH(i, argv+1) { _cleanup_free_ char *m = NULL; - if (!service_name_is_valid(*i)) + if (!sd_bus_service_name_is_valid(*i)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name '%s'", *i); m = strjoin("sender='", *i, "'"); @@ -1313,7 +1322,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f r = sd_bus_message_read(m, "s", &name); if (r < 0) - return log_error_errno(r, "Failed to read lost name: %m"); + return bus_log_parse_error(r); if (streq(name, unique_name)) is_monitor = true; @@ -1376,6 +1385,8 @@ static int status(int argc, char **argv, void *userdata) { if (r < 0) return r; + (void) pager_open(arg_pager_flags); + if (!isempty(argv[1])) { r = parse_pid(argv[1], &pid); if (r < 0) @@ -1403,7 +1414,8 @@ static int status(int argc, char **argv, void *userdata) { r = sd_bus_get_bus_id(bus, &bus_id); if (r >= 0) - printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal()); + printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", + ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal()); r = sd_bus_get_owner_creds( bus, @@ -1563,7 +1575,6 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char return log_error_errno(r, "Invalid array signature: %m"); { - unsigned i; char s[k + 1]; memcpy(s, signature, k); s[k] = 0; @@ -1572,7 +1583,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char if (r < 0) return bus_log_create_error(r); - for (i = 0; i < n; i++) { + for (unsigned i = 0; i < n; i++) { r = message_append_cmdline(m, s, &p); if (r < 0) return r; @@ -1613,7 +1624,9 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char memcpy(s, signature + 1, k - 2); s[k - 2] = 0; - r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + const char ctype = t == SD_BUS_TYPE_STRUCT_BEGIN ? + SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY; + r = sd_bus_message_open_container(m, ctype, s); if (r < 0) return bus_log_create_error(r); @@ -2034,7 +2047,8 @@ static int call(int argc, char **argv, void *userdata) { return r; if (*p) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many parameters for signature."); } if (!arg_expect_reply) { @@ -2122,7 +2136,8 @@ static int emit_signal(int argc, char **argv, void *userdata) { return r; if (*p) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many parameters for signature."); } r = sd_bus_send(bus, m, NULL); @@ -2147,7 +2162,9 @@ static int get_property(int argc, char **argv, void *userdata) { const char *contents = NULL; char type; - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); + r = sd_bus_call_method(bus, argv[1], argv[2], + "org.freedesktop.DBus.Properties", "Get", + &error, &reply, "ss", argv[3], *i); if (r < 0) return log_error_errno(r, "Failed to get property %s on interface %s: %s", *i, argv[3], @@ -2209,7 +2226,8 @@ static int set_property(int argc, char **argv, void *userdata) { if (r < 0) return r; - r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set"); + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], + "org.freedesktop.DBus.Properties", "Set"); if (r < 0) return bus_log_create_error(r); @@ -2559,7 +2577,6 @@ static int parse_argv(int argc, char *argv[]) { } static int busctl_main(int argc, char *argv[]) { - static const Verb verbs[] = { { "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names }, { "status", VERB_ANY, 2, 0, status }, @@ -2581,9 +2598,7 @@ static int busctl_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b55d7299c..e09adb8b5 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -164,9 +164,7 @@ static void show_cg_info(const char *controller, const char *path) { static int run(int argc, char *argv[]) { int r, output_flags; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) @@ -199,7 +197,7 @@ static int run(int argc, char *argv[]) { arg_show_unit == SHOW_UNIT_USER, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); } q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index de25aaae5..e6c09d1b3 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -908,9 +908,7 @@ static int run(int argc, char *argv[]) { CGroupMask mask; int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/core/apparmor-setup.c b/src/core/apparmor-setup.c new file mode 100644 index 000000000..6cba841a6 --- /dev/null +++ b/src/core/apparmor-setup.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#if HAVE_APPARMOR +# include +#endif +#include + +#include "apparmor-setup.h" +#include "apparmor-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "log.h" +#include "macro.h" +#include "string-util.h" +#include "strv.h" + +#if HAVE_APPARMOR +DEFINE_TRIVIAL_CLEANUP_FUNC(aa_policy_cache *, aa_policy_cache_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(aa_features *, aa_features_unref); +#endif + +int mac_apparmor_setup(void) { +#if HAVE_APPARMOR + int r; + _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; + + if (!mac_apparmor_use()) { + log_debug("AppArmor either not supported by the kernel or disabled."); + return 0; + } + + /* To enable LSM stacking a patch to the kernel is proposed to create a + * per-LSM subdirectory to distinguish between the LSMs. Therefore, we + * read the file from the LSM specific directory first and only if that + * fails the one from the generic directory. + */ + FOREACH_STRING(current_file, "/proc/self/attr/apparmor/current", "/proc/self/attr/current") { + r = read_one_line_file(current_file, ¤t_profile); + if (r == -ENOENT) + continue; + else if (r < 0) + log_warning_errno(r, "Failed to read current AppArmor profile from file %s, ignoring: %m", current_file); + else + break; + } + if (!current_profile) { + log_warning("Failed to get the current AppArmor profile of systemd from /proc/self/attr/apparmor/current or /proc/self/attr/current, ignoring."); + return 0; + } + if (!streq(current_profile, "unconfined")) { + log_debug("We are already confined in an AppArmor profile."); + return 0; + } + + r = aa_features_new_from_kernel(&features); + if (r < 0) { + log_warning_errno(errno, "Failed to get the AppArmor feature set from the kernel, ignoring: %m"); + return 0; + } + cache_dir_path = aa_policy_cache_dir_path_preview(features, AT_FDCWD, "/etc/apparmor/earlypolicy"); + if (!cache_dir_path) { + log_debug_errno(errno, "Failed to get the path of the early AppArmor policy cache directory."); + return 0; + } + + /* aa_policy_cache_new will internally use the same path as aa_policy_cache_dir_path_preview has returned. */ + r = aa_policy_cache_new(&policy_cache, features, AT_FDCWD, "/etc/apparmor/earlypolicy", 0); + if (r < 0) { + if (errno == ENOENT) { + log_debug_errno(errno, "The early AppArmor policy cache directory %s does not exist.", cache_dir_path); + return 0; + } + log_warning_errno(errno, "Failed to create a new AppArmor policy cache, ignoring: %m"); + return 0; + } + r = aa_policy_cache_replace_all(policy_cache, NULL); + if (r < 0) { + log_warning_errno(errno, "Failed to load the profiles from the early AppArmor policy cache directory %s, ignoring: %m", cache_dir_path); + return 0; + } + + log_info("Successfully loaded all binary profiles from AppArmor early policy cache at %s.", cache_dir_path); + + r = aa_change_profile("systemd"); + if (r < 0) { + if (errno == ENOENT) + log_debug_errno(errno, "Failed to change to AppArmor profile 'systemd'. Please ensure that one of the binary profile files in policy cache directory %s contains a profile with that name.", cache_dir_path); + else + log_error_errno(errno, "Failed to change to AppArmor profile 'systemd': %m"); + return 0; + } + + log_info("Changed to AppArmor profile systemd."); +#endif + return 0; +} diff --git a/src/core/apparmor-setup.h b/src/core/apparmor-setup.h new file mode 100644 index 000000000..100680a59 --- /dev/null +++ b/src/core/apparmor-setup.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +int mac_apparmor_setup(void); diff --git a/src/core/automount.c b/src/core/automount.c index 68dc5db71..1f0519876 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -912,13 +912,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu if (safe_atou(value, &token) < 0) log_unit_debug(u, "Failed to parse token value: %s", value); else { - r = set_ensure_allocated(&a->tokens, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - r = set_put(a->tokens, UINT_TO_PTR(token)); + r = set_ensure_put(&a->tokens, NULL, UINT_TO_PTR(token)); if (r < 0) log_unit_error_errno(u, r, "Failed to add token to set: %m"); } @@ -928,13 +922,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu if (safe_atou(value, &token) < 0) log_unit_debug(u, "Failed to parse token value: %s", value); else { - r = set_ensure_allocated(&a->expire_tokens, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - r = set_put(a->expire_tokens, UINT_TO_PTR(token)); + r = set_ensure_put(&a->expire_tokens, NULL, UINT_TO_PTR(token)); if (r < 0) log_unit_error_errno(u, r, "Failed to add expire token to set: %m"); } @@ -1010,13 +998,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo } else log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where); - r = set_ensure_allocated(&a->tokens, NULL); - if (r < 0) { - log_unit_error(UNIT(a), "Failed to allocate token set."); - goto fail; - } - - r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); + r = set_ensure_put(&a->tokens, NULL, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); if (r < 0) { log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); goto fail; @@ -1030,13 +1012,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo automount_stop_expire(a); - r = set_ensure_allocated(&a->expire_tokens, NULL); - if (r < 0) { - log_unit_error(UNIT(a), "Failed to allocate token set."); - goto fail; - } - - r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); + r = set_ensure_put(&a->expire_tokens, NULL, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); if (r < 0) { log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); goto fail; @@ -1138,7 +1114,6 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, - .bus_vtable = bus_automount_vtable, .bus_set_property = bus_automount_set_property, .shutdown = automount_shutdown, diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c index 07ef9f677..34320e88f 100644 --- a/src/core/bpf-devices.c +++ b/src/core/bpf-devices.c @@ -38,7 +38,7 @@ static int bpf_access_type(const char *acc) { return r; } -static int bpf_prog_whitelist_device( +static int bpf_prog_allow_list_device( BPFProgram *prog, char type, int major, @@ -80,7 +80,7 @@ static int bpf_prog_whitelist_device( return r; } -static int bpf_prog_whitelist_major( +static int bpf_prog_allow_list_major( BPFProgram *prog, char type, int major, @@ -120,7 +120,7 @@ static int bpf_prog_whitelist_major( return r; } -static int bpf_prog_whitelist_class( +static int bpf_prog_allow_list_class( BPFProgram *prog, char type, const char *acc) { @@ -161,7 +161,7 @@ static int bpf_prog_whitelist_class( int bpf_devices_cgroup_init( BPFProgram **ret, CGroupDevicePolicy policy, - bool whitelist) { + bool allow_list) { const struct bpf_insn pre_insn[] = { /* load device type to r2 */ @@ -188,14 +188,14 @@ int bpf_devices_cgroup_init( assert(ret); - if (policy == CGROUP_DEVICE_POLICY_AUTO && !whitelist) + if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list) return 0; r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog); if (r < 0) return log_error_errno(r, "Loading device control BPF program failed: %m"); - if (policy == CGROUP_DEVICE_POLICY_CLOSED || whitelist) { + if (policy == CGROUP_DEVICE_POLICY_CLOSED || allow_list) { r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn)); if (r < 0) return log_error_errno(r, "Extending device control BPF program failed: %m"); @@ -209,7 +209,7 @@ int bpf_devices_cgroup_init( int bpf_devices_apply_policy( BPFProgram *prog, CGroupDevicePolicy policy, - bool whitelist, + bool allow_list, const char *cgroup_path, BPFProgram **prog_installed) { @@ -221,7 +221,7 @@ int bpf_devices_apply_policy( if (!prog) goto finish; - const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !whitelist; + const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !allow_list; const struct bpf_insn post_insn[] = { /* return DENY */ @@ -325,7 +325,7 @@ int bpf_devices_supported(void) { return supported = 1; } -static int whitelist_device_pattern( +static int allow_list_device_pattern( BPFProgram *prog, const char *path, char type, @@ -340,11 +340,11 @@ static int whitelist_device_pattern( return 0; if (maj && min) - return bpf_prog_whitelist_device(prog, type, *maj, *min, acc); + return bpf_prog_allow_list_device(prog, type, *maj, *min, acc); else if (maj) - return bpf_prog_whitelist_major(prog, type, *maj, acc); + return bpf_prog_allow_list_major(prog, type, *maj, acc); else - return bpf_prog_whitelist_class(prog, type, acc); + return bpf_prog_allow_list_class(prog, type, acc); } else { char buf[2+DECIMAL_STR_MAX(unsigned)*2+2+4]; @@ -369,7 +369,7 @@ static int whitelist_device_pattern( } } -int bpf_devices_whitelist_device( +int bpf_devices_allow_list_device( BPFProgram *prog, const char *path, const char *node, @@ -405,10 +405,10 @@ int bpf_devices_whitelist_device( } unsigned maj = major(rdev), min = minor(rdev); - return whitelist_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc); + return allow_list_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc); } -int bpf_devices_whitelist_major( +int bpf_devices_allow_list_major( BPFProgram *prog, const char *path, const char *name, @@ -424,12 +424,12 @@ int bpf_devices_whitelist_major( if (streq(name, "*")) /* If the name is a wildcard, then apply this list to all devices of this type */ - return whitelist_device_pattern(prog, path, type, NULL, NULL, acc); + return allow_list_device_pattern(prog, path, type, NULL, NULL, acc); if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj)) /* The name is numeric and suitable as major. In that case, let's take its major, and create * the entry directly. */ - return whitelist_device_pattern(prog, path, type, &maj, NULL, acc); + return allow_list_device_pattern(prog, path, type, &maj, NULL, acc); _cleanup_fclose_ FILE *f = NULL; bool good = false, any = false; @@ -486,17 +486,17 @@ int bpf_devices_whitelist_major( continue; any = true; - (void) whitelist_device_pattern(prog, path, type, &maj, NULL, acc); + (void) allow_list_device_pattern(prog, path, type, &maj, NULL, acc); } if (!any) return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), - "Device whitelist pattern \"%s\" did not match anything.", name); + "Device allow list pattern \"%s\" did not match anything.", name); return 0; } -int bpf_devices_whitelist_static( +int bpf_devices_allow_list_static( BPFProgram *prog, const char *path) { @@ -515,13 +515,13 @@ int bpf_devices_whitelist_static( const char *node, *acc; NULSTR_FOREACH_PAIR(node, acc, auto_devices) { - k = bpf_devices_whitelist_device(prog, path, node, acc); + k = bpf_devices_allow_list_device(prog, path, node, acc); if (r >= 0 && k < 0) r = k; } /* PTS (/dev/pts) devices may not be duplicated, but accessed */ - k = bpf_devices_whitelist_major(prog, path, "pts", 'c', "rw"); + k = bpf_devices_allow_list_major(prog, path, "pts", 'c', "rw"); if (r >= 0 && k < 0) r = k; diff --git a/src/core/bpf-devices.h b/src/core/bpf-devices.h index 4a5f4b1fb..e2a08016e 100644 --- a/src/core/bpf-devices.h +++ b/src/core/bpf-devices.h @@ -7,15 +7,15 @@ typedef struct BPFProgram BPFProgram; -int bpf_devices_cgroup_init(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist); +int bpf_devices_cgroup_init(BPFProgram **ret, CGroupDevicePolicy policy, bool allow_list); int bpf_devices_apply_policy( BPFProgram *prog, CGroupDevicePolicy policy, - bool whitelist, + bool allow_list, const char *cgroup_path, BPFProgram **prog_installed); int bpf_devices_supported(void); -int bpf_devices_whitelist_device(BPFProgram *prog, const char *path, const char *node, const char *acc); -int bpf_devices_whitelist_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc); -int bpf_devices_whitelist_static(BPFProgram *prog, const char *path); +int bpf_devices_allow_list_device(BPFProgram *prog, const char *path, const char *node, const char *acc); +int bpf_devices_allow_list_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc); +int bpf_devices_allow_list_static(BPFProgram *prog, const char *path); diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index 96c1a28b4..bceb049b5 100644 --- a/src/core/bpf-firewall.c +++ b/src/core/bpf-firewall.c @@ -544,7 +544,7 @@ int bpf_firewall_compile(Unit *u) { "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units."); /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves, - * but we reuse the the accounting maps. That way the firewall in effect always maps to the actual + * 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 */ u->ip_bpf_ingress = bpf_program_unref(u->ip_bpf_ingress); @@ -595,7 +595,7 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set set_clear(*set); STRV_FOREACH(bpf_fs_path, filter_paths) { - _cleanup_free_ BPFProgram *prog = NULL; + _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; int r; r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog); @@ -606,14 +606,9 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set if (r < 0) return log_unit_error_errno(u, r, "Loading of ingress BPF program %s failed: %m", *bpf_fs_path); - r = set_ensure_allocated(set, &filter_prog_hash_ops); - if (r < 0) - return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m"); - - r = set_put(*set, prog); + r = set_ensure_consume(set, &filter_prog_hash_ops, TAKE_PTR(prog)); if (r < 0) return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m"); - TAKE_PTR(prog); } return 0; @@ -662,12 +657,9 @@ static int attach_custom_bpf_progs(Unit *u, const char *path, int attach_type, S r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI); if (r < 0) return log_unit_error_errno(u, r, "Attaching custom egress BPF program to cgroup %s failed: %m", path); - /* Remember that these BPF programs are installed now. */ - r = set_ensure_allocated(set_installed, &filter_prog_hash_ops); - if (r < 0) - return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m"); - r = set_put(*set_installed, prog); + /* Remember that these BPF programs are installed now. */ + r = set_ensure_put(set_installed, &filter_prog_hash_ops, prog); if (r < 0) return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m"); bpf_program_ref(prog); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index ddd3f4081..031b28a68 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -16,7 +16,9 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "io-util.h" #include "limits-util.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -215,31 +217,12 @@ void cgroup_context_done(CGroupContext *c) { } static int unit_get_kernel_memory_limit(Unit *u, const char *file, uint64_t *ret) { - _cleanup_free_ char *raw_kval = NULL; - uint64_t kval; - int r; - assert(u); if (!u->cgroup_realized) return -EOWNERDEAD; - r = cg_get_attribute("memory", u->cgroup_path, file, &raw_kval); - if (r < 0) - return r; - - if (streq(raw_kval, "max")) { - *ret = CGROUP_LIMIT_MAX; - return 0; - } - - r = safe_atou64(raw_kval, &kval); - if (r < 0) - return r; - - *ret = kval; - - return 0; + return cg_get_attribute_as_uint64("memory", u->cgroup_path, file, ret); } static int unit_compare_memory_limit(Unit *u, const char *property_name, uint64_t *ret_unit_value, uint64_t *ret_kernel_value) { @@ -1007,12 +990,12 @@ static int cgroup_apply_devices(Unit *u) { "Failed to reset devices.allow/devices.deny: %m"); } - bool whitelist_static = policy == CGROUP_DEVICE_POLICY_CLOSED || + bool allow_list_static = policy == CGROUP_DEVICE_POLICY_CLOSED || (policy == CGROUP_DEVICE_POLICY_AUTO && c->device_allow); - if (whitelist_static) - (void) bpf_devices_whitelist_static(prog, path); + if (allow_list_static) + (void) bpf_devices_allow_list_static(prog, path); - bool any = whitelist_static; + bool any = allow_list_static; LIST_FOREACH(device_allow, a, c->device_allow) { char acc[4], *val; unsigned k = 0; @@ -1028,11 +1011,11 @@ static int cgroup_apply_devices(Unit *u) { acc[k++] = 0; if (path_startswith(a->path, "/dev/")) - r = bpf_devices_whitelist_device(prog, path, a->path, acc); + r = bpf_devices_allow_list_device(prog, path, a->path, acc); else if ((val = startswith(a->path, "block-"))) - r = bpf_devices_whitelist_major(prog, path, val, 'b', acc); + r = bpf_devices_allow_list_major(prog, path, val, 'b', acc); else if ((val = startswith(a->path, "char-"))) - r = bpf_devices_whitelist_major(prog, path, val, 'c', acc); + r = bpf_devices_allow_list_major(prog, path, val, 'c', acc); else { log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path); continue; @@ -1046,7 +1029,7 @@ static int cgroup_apply_devices(Unit *u) { log_unit_warning_errno(u, SYNTHETIC_ERRNO(ENODEV), "No devices matched by device filter."); /* The kernel verifier would reject a program we would build with the normal intro and outro - but no whitelisting rules (outro would contain an unreachable instruction for successful + but no allow-listing rules (outro would contain an unreachable instruction for successful return). */ policy = CGROUP_DEVICE_POLICY_STRICT; } @@ -2680,6 +2663,16 @@ void unit_add_to_cgroup_empty_queue(Unit *u) { log_debug_errno(r, "Failed to enable cgroup empty event source: %m"); } +static void unit_remove_from_cgroup_empty_queue(Unit *u) { + assert(u); + + if (!u->in_cgroup_empty_queue) + return; + + LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u); + u->in_cgroup_empty_queue = false; +} + int unit_check_oom(Unit *u) { _cleanup_free_ char *oom_kill = NULL; bool increased; @@ -2780,6 +2773,41 @@ static void unit_add_to_cgroup_oom_queue(Unit *u) { log_error_errno(r, "Failed to enable cgroup oom event source: %m"); } +static int unit_check_cgroup_events(Unit *u) { + char *values[2] = {}; + int r; + + assert(u); + + r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", + STRV_MAKE("populated", "frozen"), values); + if (r < 0) + return r; + + /* The cgroup.events notifications can be merged together so act as we saw the given state for the + * first time. The functions we call to handle given state are idempotent, which makes them + * effectively remember the previous state. */ + if (values[0]) { + if (streq(values[0], "1")) + unit_remove_from_cgroup_empty_queue(u); + else + unit_add_to_cgroup_empty_queue(u); + } + + /* Disregard freezer state changes due to operations not initiated by us */ + if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) { + if (streq(values[1], "0")) + unit_thawed(u); + else + unit_frozen(u); + } + + free(values[0]); + free(values[1]); + + return 0; +} + static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { Manager *m = userdata; @@ -2816,7 +2844,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, u = hashmap_get(m->cgroup_control_inotify_wd_unit, INT_TO_PTR(e->wd)); if (u) - unit_add_to_cgroup_empty_queue(u); + unit_check_cgroup_events(u); u = hashmap_get(m->cgroup_memory_inotify_wd_unit, INT_TO_PTR(e->wd)); if (u) @@ -3112,7 +3140,6 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { } int unit_get_memory_current(Unit *u, uint64_t *ret) { - _cleanup_free_ char *v = NULL; int r; assert(u); @@ -3134,22 +3161,11 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { r = cg_all_unified(); if (r < 0) return r; - if (r > 0) - r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v); - else - r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - return safe_atou64(v, ret); + return cg_get_attribute_as_uint64("memory", u->cgroup_path, r > 0 ? "memory.current" : "memory.usage_in_bytes", ret); } int unit_get_tasks_current(Unit *u, uint64_t *ret) { - _cleanup_free_ char *v = NULL; - int r; - assert(u); assert(ret); @@ -3166,17 +3182,10 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) { if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) return -ENODATA; - r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - - return safe_atou64(v, ret); + return cg_get_attribute_as_uint64("pids", u->cgroup_path, "pids.current", ret); } static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { - _cleanup_free_ char *v = NULL; uint64_t ns; int r; @@ -3212,17 +3221,8 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { return r; ns = us * NSEC_PER_USEC; - } else { - r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - - r = safe_atou64(v, &ns); - if (r < 0) - return r; - } + } else + return cg_get_attribute_as_uint64("cpuacct", u->cgroup_path, "cpuacct.usage", ret); *ret = ns; return 0; @@ -3597,6 +3597,46 @@ int compare_job_priority(const void *a, const void *b) { return strcmp(x->unit->id, y->unit->id); } +int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { + _cleanup_free_ char *path = NULL; + FreezerState target, kernel = _FREEZER_STATE_INVALID; + int r; + + assert(u); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + + if (!u->cgroup_realized) + return -EBUSY; + + target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING; + + r = unit_freezer_state_kernel(u, &kernel); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to obtain cgroup freezer state: %m"); + + if (target == kernel) { + u->freezer_state = target; + return 0; + } + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path); + if (r < 0) + return r; + + log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing"); + + if (action == FREEZER_FREEZE) + u->freezer_state = FREEZER_FREEZING; + else + u->freezer_state = FREEZER_THAWING; + + r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return r; + + return 1; +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_DEVICE_POLICY_AUTO] = "auto", [CGROUP_DEVICE_POLICY_CLOSED] = "closed", @@ -3632,3 +3672,10 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) { } DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy); + +static const char* const freezer_action_table[_FREEZER_ACTION_MAX] = { + [FREEZER_FREEZE] = "freeze", + [FREEZER_THAW] = "thaw", +}; + +DEFINE_STRING_TABLE_LOOKUP(freezer_action, FreezerAction); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index b6bd4e0de..52d028e74 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -47,6 +47,14 @@ typedef enum CGroupDevicePolicy { _CGROUP_DEVICE_POLICY_INVALID = -1 } CGroupDevicePolicy; +typedef enum FreezerAction { + FREEZER_FREEZE, + FREEZER_THAW, + + _FREEZER_ACTION_MAX, + _FREEZER_ACTION_INVALID = -1, +} FreezerAction; + struct CGroupDeviceAllow { LIST_FIELDS(CGroupDeviceAllow, device_allow); char *path; @@ -274,3 +282,7 @@ bool unit_cgroup_delegate(Unit *u); int compare_job_priority(const void *a, const void *b); int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name); +int unit_cgroup_freezer_action(Unit *u, FreezerAction action); + +const char* freezer_action_to_string(FreezerAction a) _const_; +FreezerAction freezer_action_from_string(const char *s) _pure_; diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index bd6e6a9dd..70b85d802 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "automount.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-automount.h" #include "dbus-util.h" #include "string-util.h" diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 27dc9e43c..b7d2e3263 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -5,7 +5,7 @@ #include "af-list.h" #include "alloc-util.h" #include "bpf-firewall.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "cgroup-util.h" #include "cgroup.h" #include "dbus-cgroup.h" @@ -707,8 +707,7 @@ static int bus_cgroup_set_boolean( return 1; \ } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" +DISABLE_WARNING_TYPE_LIMITS; BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID); BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); @@ -716,7 +715,7 @@ BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEI BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1); BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0); BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0); -#pragma GCC diagnostic pop +REENABLE_WARNING; static int bus_cgroup_set_tasks_max( Unit *u, diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 93857436b..50f7ada8c 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -9,7 +9,7 @@ #include "af-list.h" #include "alloc-util.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "cap-list.h" #include "capability-util.h" #include "cpu-set-util.h" @@ -58,7 +58,6 @@ static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI) static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC); static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa); - static int property_get_environment_files( sd_bus *bus, const char *path, @@ -102,6 +101,7 @@ static int property_get_oom_score_adjust( ExecContext *c = userdata; int32_t n; + int r; assert(bus); assert(reply); @@ -113,13 +113,55 @@ static int property_get_oom_score_adjust( _cleanup_free_ char *t = NULL; n = 0; - if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) - safe_atoi32(t, &n); + r = read_one_line_file("/proc/self/oom_score_adj", &t); + if (r < 0) + log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m"); + else { + r = safe_atoi32(t, &n); + if (r < 0) + log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/oom_score_adj, ignoring: %m", t); + } } return sd_bus_message_append(reply, "i", n); } +static int property_get_coredump_filter( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + uint64_t n; + int r; + + assert(bus); + assert(reply); + assert(c); + + if (c->coredump_filter_set) + n = c->coredump_filter; + else { + _cleanup_free_ char *t = NULL; + + n = COREDUMP_FILTER_MASK_DEFAULT; + r = read_one_line_file("/proc/self/coredump_filter", &t); + if (r < 0) + log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m"); + else { + r = safe_atoux64(t, &n); + if (r < 0) + log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t); + } + } + + return sd_bus_message_append(reply, "t", n); +} + static int property_get_nice( sd_bus *bus, const char *path, @@ -328,7 +370,7 @@ static int property_get_syscall_filter( if (r < 0) return r; - r = sd_bus_message_append(reply, "b", c->syscall_whitelist); + r = sd_bus_message_append(reply, "b", c->syscall_allow_list); if (r < 0) return r; @@ -494,7 +536,7 @@ static int property_get_address_families( if (r < 0) return r; - r = sd_bus_message_append(reply, "b", c->address_families_whitelist); + r = sd_bus_message_append(reply, "b", c->address_families_allow_list); if (r < 0) return r; @@ -704,6 +746,44 @@ static int property_get_log_extra_fields( return sd_bus_message_close_container(reply); } +static int property_get_root_hash( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(c); + assert(property); + assert(reply); + + return sd_bus_message_append_array(reply, 'y', c->root_hash, c->root_hash_size); +} + +static int property_get_root_hash_sig( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(c); + assert(property); + assert(reply); + + return sd_bus_message_append_array(reply, 'y', c->root_hash_sig, c->root_hash_sig_size); +} + const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), @@ -746,7 +826,13 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST), + 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("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IOSchedulingClass", "i", property_get_ioprio_class, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IOSchedulingPriority", "i", property_get_ioprio_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -1215,6 +1301,102 @@ int bus_exec_context_set_transient_property( if (streq(name, "RootImage")) return bus_set_transient_path(u, name, &c->root_image, message, flags, error); + if (streq(name, "RootHash")) { + const void *roothash_decoded; + size_t roothash_decoded_size; + + r = sd_bus_message_read_array(message, 'y', &roothash_decoded, &roothash_decoded_size); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *encoded = NULL; + + if (roothash_decoded_size == 0) { + c->root_hash_path = mfree(c->root_hash_path); + c->root_hash = mfree(c->root_hash); + c->root_hash_size = 0; + + unit_write_settingf(u, flags, name, "RootHash="); + } else { + _cleanup_free_ void *p; + + encoded = hexmem(roothash_decoded, roothash_decoded_size); + if (!encoded) + return -ENOMEM; + + p = memdup(roothash_decoded, roothash_decoded_size); + if (!p) + return -ENOMEM; + + free_and_replace(c->root_hash, p); + c->root_hash_size = roothash_decoded_size; + c->root_hash_path = mfree(c->root_hash_path); + + unit_write_settingf(u, flags, name, "RootHash=%s", encoded); + } + } + + return 1; + } + + if (streq(name, "RootHashPath")) { + c->root_hash_size = 0; + c->root_hash = mfree(c->root_hash); + + return bus_set_transient_path(u, "RootHash", &c->root_hash_path, message, flags, error); + } + + if (streq(name, "RootHashSignature")) { + const void *roothash_sig_decoded; + size_t roothash_sig_decoded_size; + + r = sd_bus_message_read_array(message, 'y', &roothash_sig_decoded, &roothash_sig_decoded_size); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *encoded = NULL; + + if (roothash_sig_decoded_size == 0) { + c->root_hash_sig_path = mfree(c->root_hash_sig_path); + c->root_hash_sig = mfree(c->root_hash_sig); + c->root_hash_sig_size = 0; + + unit_write_settingf(u, flags, name, "RootHashSignature="); + } else { + _cleanup_free_ void *p; + ssize_t len; + + len = base64mem(roothash_sig_decoded, roothash_sig_decoded_size, &encoded); + if (len < 0) + return -ENOMEM; + + p = memdup(roothash_sig_decoded, roothash_sig_decoded_size); + if (!p) + return -ENOMEM; + + free_and_replace(c->root_hash_sig, p); + c->root_hash_sig_size = roothash_sig_decoded_size; + c->root_hash_sig_path = mfree(c->root_hash_sig_path); + + unit_write_settingf(u, flags, name, "RootHashSignature=base64:%s", encoded); + } + } + + return 1; + } + + if (streq(name, "RootHashSignaturePath")) { + c->root_hash_sig_size = 0; + c->root_hash_sig = mfree(c->root_hash_sig); + + return bus_set_transient_path(u, "RootHashSignature", &c->root_hash_sig_path, message, flags, error); + } + + if (streq(name, "RootVerity")) + return bus_set_transient_path(u, name, &c->root_verity, message, flags, error); + if (streq(name, "RootDirectory")) return bus_set_transient_path(u, name, &c->root_directory, message, flags, error); @@ -1558,14 +1740,14 @@ int bus_exec_context_set_transient_property( return bus_set_transient_errno(u, name, &c->syscall_errno, message, flags, error); if (streq(name, "SystemCallFilter")) { - int whitelist; + int allow_list; _cleanup_strv_free_ char **l = NULL; r = sd_bus_message_enter_container(message, 'r', "bas"); if (r < 0) return r; - r = sd_bus_message_read(message, "b", &whitelist); + r = sd_bus_message_read(message, "b", &allow_list); if (r < 0) return r; @@ -1579,11 +1761,11 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; - SeccompParseFlags invert_flag = whitelist ? 0 : SECCOMP_PARSE_INVERT; + SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT; char **s; if (strv_isempty(l)) { - c->syscall_whitelist = false; + c->syscall_allow_list = false; c->syscall_filter = hashmap_free(c->syscall_filter); unit_write_settingf(u, flags, name, "SystemCallFilter="); @@ -1595,14 +1777,14 @@ int bus_exec_context_set_transient_property( if (!c->syscall_filter) return log_oom(); - c->syscall_whitelist = whitelist; + c->syscall_allow_list = allow_list; - if (c->syscall_whitelist) { + if (c->syscall_allow_list) { r = seccomp_parse_syscall_filter("@default", -1, c->syscall_filter, SECCOMP_PARSE_PERMISSIVE | - SECCOMP_PARSE_WHITELIST | invert_flag, + SECCOMP_PARSE_ALLOW_LIST | invert_flag, u->id, NULL, 0); if (r < 0) @@ -1623,7 +1805,7 @@ int bus_exec_context_set_transient_property( c->syscall_filter, SECCOMP_PARSE_LOG | SECCOMP_PARSE_PERMISSIVE | invert_flag | - (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0), + (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0), u->id, NULL, 0); if (r < 0) @@ -1634,7 +1816,7 @@ int bus_exec_context_set_transient_property( if (!joined) return -ENOMEM; - unit_write_settingf(u, flags, name, "SystemCallFilter=%s%s", whitelist ? "" : "~", joined); + unit_write_settingf(u, flags, name, "SystemCallFilter=%s%s", allow_list ? "" : "~", joined); } return 1; @@ -1654,10 +1836,6 @@ int bus_exec_context_set_transient_property( else { char **s; - r = set_ensure_allocated(&c->syscall_archs, NULL); - if (r < 0) - return r; - STRV_FOREACH(s, l) { uint32_t a; @@ -1665,7 +1843,7 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - r = set_put(c->syscall_archs, UINT32_TO_PTR(a + 1)); + r = set_ensure_put(&c->syscall_archs, NULL, UINT32_TO_PTR(a + 1)); if (r < 0) return r; } @@ -1682,14 +1860,14 @@ int bus_exec_context_set_transient_property( return 1; } else if (streq(name, "RestrictAddressFamilies")) { - int whitelist; + int allow_list; _cleanup_strv_free_ char **l = NULL; r = sd_bus_message_enter_container(message, 'r', "bas"); if (r < 0) return r; - r = sd_bus_message_read(message, "b", &whitelist); + r = sd_bus_message_read(message, "b", &allow_list); if (r < 0) return r; @@ -1706,7 +1884,7 @@ int bus_exec_context_set_transient_property( char **s; if (strv_isempty(l)) { - c->address_families_whitelist = false; + c->address_families_allow_list = false; c->address_families = set_free(c->address_families); unit_write_settingf(u, flags, name, "RestrictAddressFamilies="); @@ -1718,7 +1896,7 @@ int bus_exec_context_set_transient_property( if (!c->address_families) return log_oom(); - c->address_families_whitelist = whitelist; + c->address_families_allow_list = allow_list; } STRV_FOREACH(s, l) { @@ -1728,7 +1906,7 @@ int bus_exec_context_set_transient_property( if (af < 0) return af; - if (whitelist == c->address_families_whitelist) { + if (allow_list == c->address_families_allow_list) { r = set_put(c->address_families, INT_TO_PTR(af)); if (r < 0) return r; @@ -1740,7 +1918,7 @@ int bus_exec_context_set_transient_property( if (!joined) return -ENOMEM; - unit_write_settingf(u, flags, name, "RestrictAddressFamilies=%s%s", whitelist ? "" : "~", joined); + unit_write_settingf(u, flags, name, "RestrictAddressFamilies=%s%s", allow_list ? "" : "~", joined); } return 1; @@ -2190,6 +2368,21 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "CoredumpFilter")) { + uint64_t f; + + r = sd_bus_message_read(message, "t", &f); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + c->coredump_filter = f; + c->coredump_filter_set = true; + unit_write_settingf(u, flags, name, "CoredumpFilter=0x%"PRIx64, f); + } + + return 1; + } else if (streq(name, "EnvironmentFiles")) { _cleanup_free_ char *joined = NULL; diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 6151326f7..33e412890 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -3,6 +3,7 @@ #include "sd-bus.h" #include "alloc-util.h" +#include "bus-get-properties.h" #include "bus-util.h" #include "dbus-job.h" #include "dbus-unit.h" @@ -118,9 +119,21 @@ int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_ 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("GetAfter", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetBefore", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetAfter", + NULL,, + "a(usssoo)", + SD_BUS_PARAM(jobs), + bus_job_method_get_waiting_jobs, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetBefore", + NULL,, + "a(usssoo)", + SD_BUS_PARAM(jobs), + bus_job_method_get_waiting_jobs, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST), @@ -128,6 +141,58 @@ const sd_bus_vtable bus_job_vtable[] = { SD_BUS_VTABLE_END }; +static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Job *j; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = manager_get_job_from_dbus_path(m, path, &j); + if (r < 0) + return 0; + + *found = j; + return 1; +} + +static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + Manager *m = userdata; + unsigned k = 0; + Iterator i; + Job *j; + + l = new0(char*, hashmap_size(m->jobs)+1); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(j, m->jobs, i) { + l[k] = job_dbus_path(j); + if (!l[k]) + return -ENOMEM; + + k++; + } + + assert(hashmap_size(m->jobs) == k); + + *nodes = TAKE_PTR(l); + + return k; +} + +const BusObjectImplementation job_object = { + "/org/freedesktop/systemd1/job", + "org.freedesktop.systemd1.Job", + .fallback_vtables = BUS_FALLBACK_VTABLES({bus_job_vtable, bus_job_find}), + .node_enumerator = bus_job_enumerate, +}; + static int send_new_signal(sd_bus *bus, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *p = NULL; diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h index 380e117fc..96c5b6630 100644 --- a/src/core/dbus-job.h +++ b/src/core/dbus-job.h @@ -2,11 +2,12 @@ #pragma once #include "sd-bus.h" -#include "sd-bus-vtable.h" #include "unit.h" +#include "bus-object.h" extern const sd_bus_vtable bus_job_vtable[]; +extern const BusObjectImplementation job_object; int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error); int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 30597e86f..eda341037 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-kill.h" #include "dbus-util.h" #include "kill.h" diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index ef4bb316c..07e139c5a 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -9,7 +9,8 @@ #include "architecture.h" #include "build.h" #include "bus-common-errors.h" -#include "bus-util.h" +#include "bus-get-properties.h" +#include "bus-log-control-api.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-job.h" @@ -50,7 +51,6 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_oom_policy, oom_policy, OOMPolicy) static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_version, "s", GIT_VERSION); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_features, "s", SYSTEMD_FEATURES); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_architecture, "s", architecture_to_string(uname_architecture())); -static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_log_target, "s", log_target_to_string(log_get_target())); static BUS_DEFINE_PROPERTY_GET2(property_get_system_state, "s", Manager, manager_state, manager_state_to_string); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_timer_slack_nsec, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "u", Hashmap *, hashmap_size); @@ -141,28 +141,6 @@ static int property_set_log_target( return 0; } -static int property_get_log_level( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *t = NULL; - int r; - - assert(bus); - assert(reply); - - r = log_level_to_string_alloc(log_get_max_level(), &t); - if (r < 0) - return r; - - return sd_bus_message_append(reply, "s", t); -} - static int property_set_log_level( sd_bus *bus, const char *path, @@ -256,14 +234,82 @@ static int property_get_show_status( sd_bus_error *error) { Manager *m = userdata; - int b; + assert(m); assert(bus); assert(reply); - assert(m); - b = IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES); - return sd_bus_message_append_basic(reply, 'b', &b); + return sd_bus_message_append(reply, "b", manager_get_show_status_on(m)); +} + +static int property_get_runtime_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_RUNTIME)); +} + +static int property_get_reboot_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_REBOOT)); +} + +static int property_get_kexec_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_KEXEC)); +} + +static int property_set_watchdog(Manager *m, WatchdogType type, sd_bus_message *value) { + usec_t timeout; + int r; + + assert(m); + assert(value); + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + r = sd_bus_message_read(value, "t", &timeout); + if (r < 0) + return r; + + return manager_override_watchdog(m, type, timeout); } static int property_set_runtime_watchdog( @@ -275,19 +321,37 @@ static int property_set_runtime_watchdog( void *userdata, sd_bus_error *error) { - usec_t *t = userdata; - int r; + return property_set_watchdog(userdata, WATCHDOG_RUNTIME, value); +} +static int property_set_reboot_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_REBOOT, value); +} + +static int property_set_kexec_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + _unused_ Manager *m = userdata; + + assert(m); assert(bus); assert(value); - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - r = sd_bus_message_read(value, "t", t); - if (r < 0) - return r; - - return watchdog_set_timeout(t); + return property_set_watchdog(userdata, WATCHDOG_KEXEC, value); } static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) { @@ -642,6 +706,14 @@ static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_err return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED); } +static int method_freeze_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, 0); +} + +static int method_thaw_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, 0); +} + static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { /* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the * unit to be loaded properly (since a failed unit might have its unit file disappeared) */ @@ -2378,6 +2450,30 @@ static int method_abandon_scope(sd_bus_message *message, void *userdata, sd_bus_ return bus_scope_method_abandon(message, u, error); } +static int method_set_show_status(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + ShowStatus mode = _SHOW_STATUS_INVALID; + const char *t; + int r; + + assert(m); + assert(message); + + r = sd_bus_message_read(message, "s", &t); + if (r < 0) + return r; + + if (!isempty(t)) { + mode = show_status_from_string(t); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid show status '%s'", t); + } + + manager_override_show_status(m, mode, "bus"); + + return sd_bus_reply_method_return(message, NULL); +} + const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -2404,8 +2500,8 @@ const sd_bus_vtable bus_manager_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0), - SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0), + SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, property_set_log_level, 0, 0), + SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, property_set_log_target, 0, 0), SD_BUS_PROPERTY("NNames", "u", property_get_hashmap_size, offsetof(Manager, units), 0), SD_BUS_PROPERTY("NFailedUnits", "u", property_get_set_size, offsetof(Manager, failed_units), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NJobs", "u", property_get_hashmap_size, offsetof(Manager, jobs), 0), @@ -2418,11 +2514,11 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), 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_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), - SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 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", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), SD_BUS_VTABLE_HIDDEN), - SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, kexec_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", property_get_kexec_watchdog, property_set_kexec_watchdog, 0, 0), SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), @@ -2477,91 +2573,606 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, default_oom_policy), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitByInvocationID", "ay", "o", method_get_unit_by_invocation_id, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitByControlGroup", "s", "o", method_get_unit_by_control_group, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StopUnit", "ss", "o", method_stop_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadUnit", "ss", "o", method_reload_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RestartUnit", "ss", "o", method_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CleanUnit", "sas", NULL, method_clean_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AttachProcessesToUnit", "ssau", NULL, method_attach_processes_to_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AbandonScope", "s", NULL, method_abandon_scope, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("DumpByFileDescriptor", NULL, "h", method_dump_by_fd, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), - SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), - SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0), - SD_BUS_METHOD("Reboot", NULL, NULL, method_reboot, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("PowerOff", NULL, NULL, method_poweroff, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("Halt", NULL, NULL, method_halt, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("KExec", NULL, NULL, method_kexec, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("SwitchRoot", "ss", NULL, method_switch_root, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitFileLinks", "sb", "as", method_get_unit_file_links, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetDynamicUsers", NULL, "a(us)", method_get_dynamic_users, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnit", + "s", + SD_BUS_PARAM(name), + "o", + SD_BUS_PARAM(unit), + method_get_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitByPID", + "u", + SD_BUS_PARAM(pid), + "o", + SD_BUS_PARAM(unit), + method_get_unit_by_pid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitByInvocationID", + "ay", + SD_BUS_PARAM(invocation_id), + "o", + SD_BUS_PARAM(unit), + method_get_unit_by_invocation_id, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitByControlGroup", + "s", + SD_BUS_PARAM(cgroup), + "o", + SD_BUS_PARAM(unit), + method_get_unit_by_control_group, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("LoadUnit", + "s", + SD_BUS_PARAM(name), + "o", + SD_BUS_PARAM(unit), + method_load_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("StartUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_start_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("StartUnitReplace", + "sss", + SD_BUS_PARAM(old_unit) + SD_BUS_PARAM(new_unit) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_start_unit_replace, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("StopUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_stop_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReloadUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("RestartUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_restart_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TryRestartUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_try_restart_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReloadOrRestartUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload_or_restart_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestartUnit", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload_or_try_restart_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("EnqueueUnitJob", + "sss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(job_type) + SD_BUS_PARAM(job_mode), + "uososa(uosos)", + SD_BUS_PARAM(job_id) + SD_BUS_PARAM(job_path) + SD_BUS_PARAM(unit_id) + SD_BUS_PARAM(unit_path) + SD_BUS_PARAM(job_type) + SD_BUS_PARAM(affected_jobs), + method_enqueue_unit_job, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("KillUnit", + "ssi", + SD_BUS_PARAM(name) + SD_BUS_PARAM(whom) + SD_BUS_PARAM(signal), + NULL,, + method_kill_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CleanUnit", + "sas", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mask), + NULL,, + method_clean_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("FreezeUnit", + "s", + SD_BUS_PARAM(name), + NULL,, + method_freeze_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ThawUnit", + "s", + SD_BUS_PARAM(name), + NULL,, + method_thaw_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit", + "s", + SD_BUS_PARAM(name), + NULL,, + method_reset_failed_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetUnitProperties", + "sba(sv)", + SD_BUS_PARAM(name) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(properties), + NULL,, + method_set_unit_properties, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("RefUnit", + "s", + SD_BUS_PARAM(name), + NULL,, + method_ref_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("UnrefUnit", + "s", + SD_BUS_PARAM(name), + NULL,, + method_unref_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("StartTransientUnit", + "ssa(sv)a(sa(sv))", + SD_BUS_PARAM(name) + SD_BUS_PARAM(mode) + SD_BUS_PARAM(properties) + SD_BUS_PARAM(aux), + "o", + SD_BUS_PARAM(job), + method_start_transient_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitProcesses", + "s", + SD_BUS_PARAM(name), + "a(sus)", + SD_BUS_PARAM(processes), + method_get_unit_processes, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("AttachProcessesToUnit", + "ssau", + SD_BUS_PARAM(unit_name) + SD_BUS_PARAM(subcgroup) + SD_BUS_PARAM(pids), + NULL,, + method_attach_processes_to_unit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("AbandonScope", + "s", + SD_BUS_PARAM(name), + NULL,, + method_abandon_scope, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetJob", + "u", + SD_BUS_PARAM(id), + "o", + SD_BUS_PARAM(job), + method_get_job, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetJobAfter", + "u", + SD_BUS_PARAM(id), + "a(usssoo)", + SD_BUS_PARAM(jobs), + method_get_job_waiting, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetJobBefore", + "u", + SD_BUS_PARAM(id), + "a(usssoo)", + SD_BUS_PARAM(jobs), + method_get_job_waiting, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CancelJob", + "u", + SD_BUS_PARAM(id), + NULL,, + method_cancel_job, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ClearJobs", + NULL, + NULL, + method_clear_jobs, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailed", + NULL, + NULL, + method_reset_failed, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetShowStatus", + "s", + SD_BUS_PARAM(mode), + NULL,, + method_set_show_status, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_ADMIN)), + SD_BUS_METHOD_WITH_NAMES("ListUnits", + NULL,, + "a(ssssssouso)", + SD_BUS_PARAM(units), + method_list_units, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUnitsFiltered", + "as", + SD_BUS_PARAM(states), + "a(ssssssouso)", + SD_BUS_PARAM(units), + method_list_units_filtered, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUnitsByPatterns", + "asas", + SD_BUS_PARAM(states) + SD_BUS_PARAM(patterns), + "a(ssssssouso)", + SD_BUS_PARAM(units), + method_list_units_by_patterns, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUnitsByNames", + "as", + SD_BUS_PARAM(names), + "a(ssssssouso)", + SD_BUS_PARAM(units), + method_list_units_by_names, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListJobs", + NULL,, + "a(usssoo)", + SD_BUS_PARAM(jobs), + method_list_jobs, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Subscribe", + NULL, + NULL, + method_subscribe, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unsubscribe", + NULL, + NULL, + method_unsubscribe, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Dump", + NULL,, + "s", + SD_BUS_PARAM(output), + method_dump, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("DumpByFileDescriptor", + NULL,, + "h", + SD_BUS_PARAM(fd), + method_dump_by_fd, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CreateSnapshot", + "sb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(cleanup), + "o", + SD_BUS_PARAM(unit), + method_refuse_snapshot, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), + SD_BUS_METHOD_WITH_NAMES("RemoveSnapshot", + "s", + SD_BUS_PARAM(name), + NULL,, + method_refuse_snapshot, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN), + SD_BUS_METHOD("Reload", + NULL, + NULL, + method_reload, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Reexecute", + NULL, + NULL, + method_reexecute, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Exit", + NULL, + NULL, + method_exit, + 0), + SD_BUS_METHOD("Reboot", + NULL, + NULL, + method_reboot, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("PowerOff", + NULL, + NULL, + method_poweroff, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("Halt", + NULL, + NULL, + method_halt, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("KExec", + NULL, + NULL, + method_kexec, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD_WITH_NAMES("SwitchRoot", + "ss", + SD_BUS_PARAM(new_root) + SD_BUS_PARAM(init), + NULL,, + method_switch_root, + SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD_WITH_NAMES("SetEnvironment", + "as", + SD_BUS_PARAM(assignments), + NULL,, + method_set_environment, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("UnsetEnvironment", + "as", + SD_BUS_PARAM(names), + NULL,, + method_unset_environment, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("UnsetAndSetEnvironment", + "asas", + SD_BUS_PARAM(names) + SD_BUS_PARAM(assignments), + NULL,, + method_unset_and_set_environment, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUnitFiles", + NULL,, + "a(ss)", + SD_BUS_PARAM(unit_files), + method_list_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUnitFilesByPatterns", + "asas", + SD_BUS_PARAM(states) + SD_BUS_PARAM(patterns), + "a(ss)", + SD_BUS_PARAM(unit_files), + method_list_unit_files_by_patterns, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitFileState", + "s", + SD_BUS_PARAM(file), + "s", + SD_BUS_PARAM(state), + method_get_unit_file_state, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("EnableUnitFiles", + "asbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "ba(sss)", + SD_BUS_PARAM(carries_install_info) + SD_BUS_PARAM(changes), + method_enable_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("DisableUnitFiles", + "asb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime), + "a(sss)", + SD_BUS_PARAM(changes), + method_disable_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReenableUnitFiles", + "asbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "ba(sss)", + SD_BUS_PARAM(carries_install_info) + SD_BUS_PARAM(changes), + method_reenable_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("LinkUnitFiles", + "asbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "a(sss)", + SD_BUS_PARAM(changes), + method_link_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PresetUnitFiles", + "asbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "ba(sss)", + SD_BUS_PARAM(carries_install_info) + SD_BUS_PARAM(changes), + method_preset_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PresetUnitFilesWithMode", + "assbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(mode) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "ba(sss)", + SD_BUS_PARAM(carries_install_info) + SD_BUS_PARAM(changes), + method_preset_unit_files_with_mode, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MaskUnitFiles", + "asbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "a(sss)", + SD_BUS_PARAM(changes), + method_mask_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("UnmaskUnitFiles", + "asb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(runtime), + "a(sss)", + SD_BUS_PARAM(changes), + method_unmask_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("RevertUnitFiles", + "as", + SD_BUS_PARAM(files), + "a(sss)", + SD_BUS_PARAM(changes), + method_revert_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetDefaultTarget", + "sb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(force), + "a(sss)", + SD_BUS_PARAM(changes), + method_set_default_target, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetDefaultTarget", + NULL,, + "s", + SD_BUS_PARAM(name), + method_get_default_target, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PresetAllUnitFiles", + "sbb", + SD_BUS_PARAM(mode) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "a(sss)", + SD_BUS_PARAM(changes), + method_preset_all_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("AddDependencyUnitFiles", + "asssbb", + SD_BUS_PARAM(files) + SD_BUS_PARAM(target) + SD_BUS_PARAM(type) + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(force), + "a(sss)", + SD_BUS_PARAM(changes), + method_add_dependency_unit_files, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUnitFileLinks", + "sb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(runtime), + "as", + SD_BUS_PARAM(links), + method_get_unit_file_links, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetExitCode", + "y", + SD_BUS_PARAM(number), + NULL,, + method_set_exit_code, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByName", + "s", + SD_BUS_PARAM(name), + "u", + SD_BUS_PARAM(uid), + method_lookup_dynamic_user_by_name, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByUID", + "u", + SD_BUS_PARAM(uid), + "s", + SD_BUS_PARAM(name), + method_lookup_dynamic_user_by_uid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetDynamicUsers", + NULL,, + "a(us)", + SD_BUS_PARAM(users), + method_get_dynamic_users, + SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("UnitNew", "so", 0), - SD_BUS_SIGNAL("UnitRemoved", "so", 0), - SD_BUS_SIGNAL("JobNew", "uos", 0), - SD_BUS_SIGNAL("JobRemoved", "uoss", 0), - SD_BUS_SIGNAL("StartupFinished", "tttttt", 0), + SD_BUS_SIGNAL_WITH_NAMES("UnitNew", + "so", + SD_BUS_PARAM(id) + SD_BUS_PARAM(unit), + 0), + SD_BUS_SIGNAL_WITH_NAMES("UnitRemoved", + "so", + SD_BUS_PARAM(id) + SD_BUS_PARAM(unit), + 0), + SD_BUS_SIGNAL_WITH_NAMES("JobNew", + "uos", + SD_BUS_PARAM(id) + SD_BUS_PARAM(job) + SD_BUS_PARAM(unit), + 0), + SD_BUS_SIGNAL_WITH_NAMES("JobRemoved", + "uoss", + SD_BUS_PARAM(id) + SD_BUS_PARAM(job) + SD_BUS_PARAM(unit) + SD_BUS_PARAM(result), + 0), + SD_BUS_SIGNAL_WITH_NAMES("StartupFinished", + "tttttt", + SD_BUS_PARAM(firmware) + SD_BUS_PARAM(loader) + SD_BUS_PARAM(kernel) + SD_BUS_PARAM(initrd) + SD_BUS_PARAM(userspace) + SD_BUS_PARAM(total), + 0), SD_BUS_SIGNAL("UnitFilesChanged", NULL, 0), - SD_BUS_SIGNAL("Reloading", "b", 0), + SD_BUS_SIGNAL_WITH_NAMES("Reloading", + "b", + SD_BUS_PARAM(active), + 0), SD_BUS_VTABLE_END }; +const sd_bus_vtable bus_manager_log_control_vtable[] = { + SD_BUS_VTABLE_START(0), + + /* We define a private version of this interface here, since we want slightly different + * implementations for the setters. We'll still use the generic getters however, and we share the + * setters with the implementations for the Manager interface above (which pre-dates the generic + * service API interface). */ + + SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, property_set_log_level, 0, 0), + SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, property_set_log_target, 0, 0), + SD_BUS_PROPERTY("SyslogIdentifier", "s", bus_property_get_syslog_identifier, 0, 0), + + SD_BUS_VTABLE_END, +}; + static int send_finished(sd_bus *bus, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; usec_t *times = userdata; diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h index 10aa2ecce..83854b0f5 100644 --- a/src/core/dbus-manager.h +++ b/src/core/dbus-manager.h @@ -6,6 +6,7 @@ #include "manager.h" extern const sd_bus_vtable bus_manager_vtable[]; +extern const sd_bus_vtable bus_manager_log_control_vtable[]; void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); void bus_manager_send_reloading(Manager *m, bool active); diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index b6d61627e..bab12cc4f 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" @@ -51,6 +51,7 @@ const sd_bus_vtable bus_mount_vtable[] = { SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ReadWriteOnly", "b", bus_property_get_bool, offsetof(Mount, read_write_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -102,6 +103,9 @@ static int bus_mount_set_transient_property( if (streq(name, "ForceUnmount")) return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error); + if (streq(name, "ReadWriteOnly")) + return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error); + return 0; } diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index 1a97d6248..76cd9d326 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-path.h" #include "dbus-util.h" #include "list.h" diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index 84d91dcfa..aecfda653 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -2,8 +2,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" -#include "bus-internal.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-kill.h" #include "dbus-scope.h" @@ -140,7 +139,7 @@ static int bus_scope_set_transient_property( if (r < 0) return r; - if (!isempty(controller) && !service_name_is_valid(controller)) + if (!isempty(controller) && !sd_bus_service_name_is_valid(controller)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Controller '%s' is not a valid bus name.", controller); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 5cf9b2189..3cc453dff 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -4,8 +4,7 @@ #include "alloc-util.h" #include "async.h" -#include "bus-internal.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" @@ -29,6 +28,8 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, Servi static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); static BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, service_timeout_abort_usec); +static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode); static int property_get_exit_status_set( sd_bus *bus, @@ -101,8 +102,10 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0), + SD_BUS_PROPERTY("TimeoutStartFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_start_failure_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStopFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_stop_failure_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("WatchdogUSec", "t", property_get_watchdog_usec, 0, 0), BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* 😷 deprecated */ SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), @@ -258,7 +261,8 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string); -static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, service_name_is_valid); +static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid); +static BUS_DEFINE_SET_TRANSIENT_PARSE(timeout_failure_mode, ServiceTimeoutFailureMode, service_timeout_failure_mode_from_string); static int bus_service_set_transient_property( Service *s, @@ -316,6 +320,12 @@ static int bus_service_set_transient_property( return r; } + if (streq(name, "TimeoutStartFailureMode")) + return bus_set_transient_timeout_failure_mode(u, name, &s->timeout_start_failure_mode, message, flags, error); + + if (streq(name, "TimeoutStopFailureMode")) + return bus_set_transient_timeout_failure_mode(u, name, &s->timeout_stop_failure_mode, message, flags, error); + if (streq(name, "RuntimeMaxUSec")) return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index ad7b41a95..f01489e29 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" @@ -104,6 +104,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), @@ -202,6 +203,9 @@ static int bus_socket_set_transient_property( if (streq(name, "PassSecurity")) return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error); + if (streq(name, "PassPacketInfo")) + return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error); + if (streq(name, "ReusePort")) return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error); diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 57c8c4209..cb4824b6b 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -3,7 +3,7 @@ Copyright © 2010 Maarten Lankhorst ***/ -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-swap.h" diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 439c276fa..da35fa867 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" -#include "bus-util.h" +#include "bus-get-properties.h" #include "dbus-timer.h" #include "dbus-util.h" #include "strv.h" diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 496fd5eb6..9e9d3b101 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -5,8 +5,8 @@ #include "alloc-util.h" #include "bpf-firewall.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" #include "bus-polkit.h" -#include "bus-util.h" #include "cgroup-util.h" #include "condition.h" #include "dbus-job.h" @@ -46,12 +46,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description); static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string); +static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string); static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string); static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string); static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload); static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual); static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual); static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual); +static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze); static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0); @@ -100,20 +102,24 @@ static int property_get_names( void *userdata, sd_bus_error *error) { - Set **s = userdata; + Unit *u = userdata; Iterator i; const char *t; int r; assert(bus); assert(reply); - assert(s); + assert(u); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) return r; - SET_FOREACH(t, *s, i) { + r = sd_bus_message_append(reply, "s", u->id); + if (r < 0) + return r; + + SET_FOREACH(t, u->aliases, i) { r = sd_bus_message_append(reply, "s", t); if (r < 0) return r; @@ -724,6 +730,79 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error return sd_bus_reply_method_return(message, NULL); } +static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) { + const char* perm; + int (*method)(Unit*); + Unit *u = userdata; + bool reply_no_delay = false; + int r; + + assert(message); + assert(u); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + + if (action == FREEZER_FREEZE) { + perm = "stop"; + method = unit_freeze; + } else { + perm = "start"; + method = unit_thaw; + } + + r = mac_selinux_unit_access_check(u, message, perm, error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async_full( + u, + perm, + CAP_SYS_ADMIN, + N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."), + true, + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = method(u); + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id); + if (r == -EBUSY) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job."); + if (r == -EHOSTDOWN) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive."); + if (r == -EALREADY) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id); + if (r < 0) + return r; + if (r == 0) + reply_no_delay = true; + + assert(!u->pending_freezer_message); + + r = sd_bus_message_new_method_return(message, &u->pending_freezer_message); + if (r < 0) + return r; + + if (reply_no_delay) { + r = bus_unit_send_pending_freezer_message(u); + if (r < 0) + return r; + } + + return 1; +} + +int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW); +} + +int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE); +} + static int property_get_refs( sd_bus *bus, const char *path, @@ -766,7 +845,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Names", "as", property_get_names, offsetof(Unit, names), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0), SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -793,6 +872,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST), @@ -809,6 +889,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST), @@ -843,20 +924,113 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Refs", "as", property_get_refs, 0, 0), - SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("EnqueueJob", "ss", "uososa(uosos)", bus_unit_method_enqueue_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Clean", "as", NULL, bus_unit_method_clean, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Start", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_start, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Stop", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_stop, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Reload", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Restart", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_restart, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TryRestart", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_try_restart, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReloadOrRestart", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload_or_restart, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestart", + "s", + SD_BUS_PARAM(mode), + "o", + SD_BUS_PARAM(job), + method_reload_or_try_restart, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("EnqueueJob", + "ss", + SD_BUS_PARAM(job_type) + SD_BUS_PARAM(job_mode), + "uososa(uosos)", + SD_BUS_PARAM(job_id) + SD_BUS_PARAM(job_path) + SD_BUS_PARAM(unit_id) + SD_BUS_PARAM(unit_path) + SD_BUS_PARAM(job_type) + SD_BUS_PARAM(affected_jobs), + bus_unit_method_enqueue_job, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Kill", + "si", + SD_BUS_PARAM(whom) + SD_BUS_PARAM(signal), + NULL,, + bus_unit_method_kill, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailed", + NULL, + NULL, + bus_unit_method_reset_failed, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetProperties", + "ba(sv)", + SD_BUS_PARAM(runtime) + SD_BUS_PARAM(properties), + NULL,, + bus_unit_method_set_properties, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Ref", + NULL, + NULL, + bus_unit_method_ref, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unref", + NULL, + NULL, + bus_unit_method_unref, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Clean", + "as", + SD_BUS_PARAM(mask), + NULL,, + bus_unit_method_clean, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Freeze", + NULL, + NULL, + bus_unit_method_freeze, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Thaw", + NULL, + NULL, + bus_unit_method_thaw, + SD_BUS_VTABLE_UNPRIVILEGED), /* For dependency types we don't support anymore always return an empty array */ SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), @@ -866,6 +1040,7 @@ const sd_bus_vtable bus_unit_vtable[] = { /* Obsolete alias names */ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_VTABLE_END }; @@ -1365,8 +1540,22 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("IOReadOperations", "t", property_get_io_counter, 0, 0), SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0), SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0), - SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD_WITH_NAMES("GetProcesses", + NULL,, + "a(sus)", + SD_BUS_PARAM(processes), + bus_unit_method_get_processes, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD_WITH_NAMES("AttachProcesses", + "sau", + SD_BUS_PARAM(subcgroup) + SD_BUS_PARAM(pids), + NULL,, + bus_unit_method_attach_processes, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END }; @@ -1468,6 +1657,23 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) { bus_unit_send_change_signal(u); } +int bus_unit_send_pending_freezer_message(Unit *u) { + int r; + + assert(u); + + if (!u->pending_freezer_message) + return 0; + + r = sd_bus_send(NULL, u->pending_freezer_message, NULL); + if (r < 0) + log_warning_errno(r, "Failed to send queued message, ignoring: %m"); + + u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); + + return 0; +} + static int send_removed_signal(sd_bus *bus, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *p = NULL; @@ -1522,7 +1728,7 @@ int bus_unit_queue_job( _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *job_path = NULL, *unit_path = NULL; - _cleanup_(set_freep) Set *affected = NULL; + _cleanup_set_free_ Set *affected = NULL; Iterator i; Job *j, *a; int r; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 91711311a..f21f23602 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -2,7 +2,6 @@ #pragma once #include "sd-bus.h" -#include "sd-bus-vtable.h" #include "unit.h" @@ -11,6 +10,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_pending_change_signal(Unit *u, bool including_new); +int bus_unit_send_pending_freezer_message(Unit *u); void bus_unit_send_removed_signal(Unit *u); int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); @@ -25,6 +25,8 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error); typedef enum BusUnitQueueFlags { BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0, diff --git a/src/core/dbus.c b/src/core/dbus.c index 50155f22c..76bb91d0e 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -274,25 +274,6 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er } #endif -static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Job *j; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = manager_get_job_from_dbus_path(m, path, &j); - if (r < 0) - return 0; - - *found = j; - return 1; -} - static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) { Unit *u = NULL; /* just to appease gcc, initialization is not really necessary */ int r; @@ -472,32 +453,6 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte return 1; } -static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - Manager *m = userdata; - unsigned k = 0; - Iterator i; - Job *j; - - l = new0(char*, hashmap_size(m->jobs)+1); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(j, m->jobs, i) { - l[k] = job_dbus_path(j); - if (!l[k]) - return -ENOMEM; - - k++; - } - - assert(hashmap_size(m->jobs) == k); - - *nodes = TAKE_PTR(l); - - return k; -} - static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; @@ -522,8 +477,147 @@ static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, cha return k; } +static const BusObjectImplementation unit_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Unit", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_unit_vtable, bus_unit_find }), + .node_enumerator = bus_unit_enumerate, +}; + +static const BusObjectImplementation bus_automount_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Automount", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_automount_vtable, bus_unit_interface_find }), +}; + +static const BusObjectImplementation bus_device_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Device", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_device_vtable, bus_unit_interface_find }), +}; + +static const BusObjectImplementation bus_mount_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Mount", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_mount_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }, + { bus_exec_vtable, bus_exec_context_find }, + { bus_kill_vtable, bus_kill_context_find }), +}; + +static const BusObjectImplementation bus_path_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Path", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_path_vtable, bus_unit_interface_find }), +}; + +static const BusObjectImplementation bus_scope_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Scope", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_scope_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }, + { bus_kill_vtable, bus_kill_context_find }), +}; + +static const BusObjectImplementation bus_service_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Service", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_service_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }, + { bus_exec_vtable, bus_exec_context_find }, + { bus_kill_vtable, bus_kill_context_find }), +}; + +static const BusObjectImplementation bus_slice_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Slice", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_slice_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }), +}; + +static const BusObjectImplementation bus_socket_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Socket", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_socket_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }, + { bus_exec_vtable, bus_exec_context_find }, + { bus_kill_vtable, bus_kill_context_find }), +}; + +static const BusObjectImplementation bus_swap_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Swap", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_swap_vtable, bus_unit_interface_find }, + { bus_unit_cgroup_vtable, bus_unit_cgroup_find }, + { bus_cgroup_vtable, bus_cgroup_context_find }, + { bus_exec_vtable, bus_exec_context_find }, + { bus_kill_vtable, bus_kill_context_find }), +}; + +static const BusObjectImplementation bus_target_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Target", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_target_vtable, bus_unit_interface_find }), +}; + +static const BusObjectImplementation bus_timer_object = { + "/org/freedesktop/systemd1/unit", + "org.freedesktop.systemd1.Timer", + .fallback_vtables = BUS_FALLBACK_VTABLES( + { bus_timer_vtable, bus_unit_interface_find }), +}; + +static const BusObjectImplementation bus_manager_object = { + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + .vtables = BUS_VTABLES(bus_manager_vtable), + .children = BUS_IMPLEMENTATIONS( + &job_object, + &unit_object, + &bus_automount_object, + &bus_device_object, + &bus_mount_object, + &bus_path_object, + &bus_scope_object, + &bus_service_object, + &bus_slice_object, + &bus_socket_object, + &bus_swap_object, + &bus_target_object, + &bus_timer_object), +}; + +static const BusObjectImplementation manager_log_control_object = { + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + .vtables = BUS_VTABLES(bus_manager_log_control_vtable), +}; + +int bus_manager_introspect_implementations(FILE *out, const char *pattern) { + return bus_introspect_implementations( + out, + pattern, + BUS_IMPLEMENTATIONS(&bus_manager_object, + &manager_log_control_object)); +} + static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { - UnitType t; int r; assert(m); @@ -535,59 +629,11 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { return log_error_errno(r, "Failed to add SELinux access filter: %m"); #endif - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m); + r = bus_add_implementation(bus, &bus_manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to register Manager vtable: %m"); + return r; - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/job", "org.freedesktop.systemd1.Job", bus_job_vtable, bus_job_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register Job vtable: %m"); - - r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/job", bus_job_enumerate, m); - if (r < 0) - return log_error_errno(r, "Failed to add job enumerator: %m"); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", "org.freedesktop.systemd1.Unit", bus_unit_vtable, bus_unit_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register Unit vtable: %m"); - - r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/unit", bus_unit_enumerate, m); - if (r < 0) - return log_error_errno(r, "Failed to add job enumerator: %m"); - - for (t = 0; t < _UNIT_TYPE_MAX; t++) { - const char *interface; - - assert_se(interface = unit_dbus_interface_from_type(t)); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); - - if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); - } - - if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); - } - - if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); - } - } - - return 0; + return bus_add_implementation(bus, &manager_log_control_object, m); } static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { @@ -956,10 +1002,15 @@ static void destroy_bus(Manager *m, sd_bus **bus) { if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus) j->bus_track = sd_bus_track_unref(j->bus_track); - HASHMAP_FOREACH(u, m->units, i) + HASHMAP_FOREACH(u, m->units, i) { if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus) u->bus_track = sd_bus_track_unref(u->bus_track); + /* Get rid of pending freezer messages on this bus */ + if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus) + u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); + } + /* Get rid of queued message on this bus */ if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus) m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message); @@ -1061,7 +1112,7 @@ int bus_foreach_bus( /* Send to all direct buses, unconditionally */ SET_FOREACH(b, m->private_buses, i) { - /* Don't bother with enqueing these messages to clients that haven't started yet */ + /* Don't bother with enqueuing these messages to clients that haven't started yet */ if (sd_bus_is_ready(b) <= 0) continue; diff --git a/src/core/dbus.h b/src/core/dbus.h index d5ba6537e..812f56ea2 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -33,3 +33,4 @@ int bus_forward_agent_released(Manager *m, const char *path); uint64_t manager_bus_n_queued_write(Manager *m); void dump_bus_properties(FILE *f); +int bus_manager_introspect_implementations(FILE *out, const char *pattern); diff --git a/src/core/device.c b/src/core/device.c index 45149e755..50d55289f 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -83,6 +83,8 @@ static int device_set_sysfs(Device *d, const char *sysfs) { } d->sysfs = TAKE_PTR(copy); + unit_add_to_dbus_queue(UNIT(d)); + return 0; } @@ -562,9 +564,6 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool if (dev && device_is_bound_by_mounts(DEVICE(u), dev)) device_upgrade_mount_deps(u); - /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */ - unit_add_to_dbus_queue(u); - return 0; fail: @@ -1081,8 +1080,6 @@ const UnitVTable device_vtable = { .active_state = device_active_state, .sub_state_to_string = device_sub_state_to_string, - .bus_vtable = bus_device_vtable, - .following = device_following, .following_set = device_following_set, diff --git a/src/core/efi-random.c b/src/core/efi-random.c index c4d25d68e..b6609e63e 100644 --- a/src/core/efi-random.c +++ b/src/core/efi-random.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include -#include -#include #include #include "alloc-util.h" @@ -11,6 +9,7 @@ #include "efivars.h" #include "fd-util.h" #include "fs-util.h" +#include "random-util.h" #include "strv.h" /* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to @@ -43,7 +42,6 @@ static void lock_down_efi_variables(void) { } int efi_take_random_seed(void) { - _cleanup_free_ struct rand_pool_info *info = NULL; _cleanup_free_ void *value = NULL; _cleanup_close_ int random_fd = -1; size_t size; @@ -79,11 +77,6 @@ int efi_take_random_seed(void) { if (size == 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring."); - /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any chance for - * confusion here. */ - if (size > INT_MAX / 8) - size = INT_MAX / 8; - random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); if (random_fd < 0) return log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m"); @@ -94,15 +87,8 @@ int efi_take_random_seed(void) { if (r < 0) return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m"); - info = malloc(offsetof(struct rand_pool_info, buf) + size); - if (!info) - return log_oom(); - - info->entropy_count = size * 8; - info->buf_size = size; - memcpy(info->buf, value, size); - - if (ioctl(random_fd, RNDADDENTROPY, info) < 0) + r = random_write_entropy(random_fd, value, size, true); + if (r < 0) return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m"); log_info("Successfully credited entropy passed from boot loader."); diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c index 4330c0f2c..1565a7992 100644 --- a/src/core/emergency-action.c +++ b/src/core/emergency-action.c @@ -12,6 +12,18 @@ #include "terminal-util.h" #include "virt.h" +static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = { + [EMERGENCY_ACTION_NONE] = "none", + [EMERGENCY_ACTION_REBOOT] = "reboot", + [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force", + [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", + [EMERGENCY_ACTION_POWEROFF] = "poweroff", + [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force", + [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate", + [EMERGENCY_ACTION_EXIT] = "exit", + [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force", +}; + static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) { log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason); if (warn) @@ -28,10 +40,22 @@ void emergency_action( int exit_status, const char *reason) { + Unit *u; + assert(m); assert(action >= 0); assert(action < _EMERGENCY_ACTION_MAX); + /* Is the special shutdown target active or queued? If so, we are in shutdown state */ + if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) { + u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); + if (u && unit_active_or_pending(u)) { + log_notice("Shutdown is already active. Skipping emergency action request %s.", + emergency_action_table[action]); + return; + } + } + if (action == EMERGENCY_ACTION_NONE) return; @@ -126,17 +150,6 @@ void emergency_action( } } -static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = { - [EMERGENCY_ACTION_NONE] = "none", - [EMERGENCY_ACTION_REBOOT] = "reboot", - [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force", - [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", - [EMERGENCY_ACTION_POWEROFF] = "poweroff", - [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force", - [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate", - [EMERGENCY_ACTION_EXIT] = "exit", - [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force", -}; DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction); int parse_emergency_action( diff --git a/src/core/execute.c b/src/core/execute.c index c1df37a09..2a4840a3a 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -54,6 +54,7 @@ #include "format-util.h" #include "fs-util.h" #include "glob-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "ioprio.h" #include "label.h" @@ -219,17 +220,10 @@ static bool is_terminal_input(ExecInput i) { static bool is_terminal_output(ExecOutput o) { return IN_SET(o, EXEC_OUTPUT_TTY, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, EXEC_OUTPUT_KMSG_AND_CONSOLE, EXEC_OUTPUT_JOURNAL_AND_CONSOLE); } -static bool is_syslog_output(ExecOutput o) { - return IN_SET(o, - EXEC_OUTPUT_SYSLOG, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE); -} - static bool is_kmsg_output(ExecOutput o) { return IN_SET(o, EXEC_OUTPUT_KMSG, @@ -361,7 +355,7 @@ static int connect_logger_as( params->flags & EXEC_PASS_LOG_UNIT ? unit->id : "", context->syslog_priority, !!context->syslog_level_prefix, - is_syslog_output(output), + false, is_kmsg_output(output), is_terminal_output(output)) < 0) return -errno; @@ -664,8 +658,6 @@ static int setup_output( /* We don't reset the terminal if this is just about output */ return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); - case EXEC_OUTPUT_SYSLOG: - case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: case EXEC_OUTPUT_KMSG: case EXEC_OUTPUT_KMSG_AND_CONSOLE: case EXEC_OUTPUT_JOURNAL: @@ -1388,14 +1380,14 @@ static void rename_process_from_path(const char *path) { static bool context_has_address_families(const ExecContext *c) { assert(c); - return c->address_families_whitelist || + return c->address_families_allow_list || !set_isempty(c->address_families); } static bool context_has_syscall_filters(const ExecContext *c) { assert(c); - return c->syscall_whitelist || + return c->syscall_allow_list || !hashmap_isempty(c->syscall_filter); } @@ -1451,7 +1443,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_ negative_action = c->syscall_errno == 0 ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno); - if (c->syscall_whitelist) { + if (c->syscall_allow_list) { default_action = negative_action; action = SCMP_ACT_ALLOW; } else { @@ -1460,7 +1452,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_ } if (needs_ambient_hack) { - r = seccomp_filter_set_add(c->syscall_filter, c->syscall_whitelist, syscall_filter_sets + SYSCALL_FILTER_SET_SETUID); + r = seccomp_filter_set_add(c->syscall_filter, c->syscall_allow_list, syscall_filter_sets + SYSCALL_FILTER_SET_SETUID); if (r < 0) return r; } @@ -1491,7 +1483,7 @@ static int apply_address_families(const Unit* u, const ExecContext *c) { if (skip_seccomp_unavailable(u, "RestrictAddressFamilies=")) return 0; - return seccomp_restrict_address_families(c->address_families, c->address_families_whitelist); + return seccomp_restrict_address_families(c->address_families, c->address_families_allow_list); } static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) { @@ -2569,7 +2561,7 @@ static bool insist_on_sandboxing( assert(n_bind_mounts == 0 || bind_mounts); /* Checks whether we need to insist on fs namespacing. i.e. whether we have settings configured that - * would alter the view on the file system beyond making things read-only or invisble, i.e. would + * would alter the view on the file system beyond making things read-only or invisible, i.e. would * rearrange stuff in a way we cannot ignore gracefully. */ if (context->n_temporary_filesystems > 0) @@ -2602,7 +2594,7 @@ static int apply_mount_namespace( char **error_path) { _cleanup_strv_free_ char **empty_directories = NULL; - char *tmp = NULL, *var = NULL; + const char *tmp_dir = NULL, *var_tmp_dir = NULL; const char *root_dir = NULL, *root_image = NULL; NamespaceInfo ns_info; bool needs_sandboxing; @@ -2627,13 +2619,19 @@ static int apply_mount_namespace( if (needs_sandboxing) { /* The runtime struct only contains the parent of the private /tmp, * which is non-accessible to world users. Inside of it there's a /tmp - * that is sticky, and that's the one we want to use here. */ + * that is sticky, and that's the one we want to use here. + * This does not apply when we are using /run/systemd/empty as fallback. */ if (context->private_tmp && runtime) { - if (runtime->tmp_dir) - tmp = strjoina(runtime->tmp_dir, "/tmp"); - if (runtime->var_tmp_dir) - var = strjoina(runtime->var_tmp_dir, "/tmp"); + if (streq_ptr(runtime->tmp_dir, RUN_SYSTEMD_EMPTY)) + tmp_dir = runtime->tmp_dir; + else if (runtime->tmp_dir) + tmp_dir = strjoina(runtime->tmp_dir, "/tmp"); + + if (streq_ptr(runtime->var_tmp_dir, RUN_SYSTEMD_EMPTY)) + var_tmp_dir = runtime->var_tmp_dir; + else if (runtime->var_tmp_dir) + var_tmp_dir = strjoina(runtime->var_tmp_dir, "/tmp"); } ns_info = (NamespaceInfo) { @@ -2671,12 +2669,15 @@ static int apply_mount_namespace( n_bind_mounts, context->temporary_filesystems, context->n_temporary_filesystems, - tmp, - var, + tmp_dir, + var_tmp_dir, context->log_namespace, needs_sandboxing ? context->protect_home : PROTECT_HOME_NO, needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO, context->mount_flags, + context->root_hash, context->root_hash_size, context->root_hash_path, + context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path, + context->root_verity, DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK, error_path); @@ -2862,7 +2863,7 @@ static int setup_keyring( } out: - /* Revert back uid & gid for the the last time, and exit */ + /* Revert back uid & gid for the last time, and exit */ /* no extra logging, as only the first already reported error matters */ if (getuid() != saved_uid) (void) setreuid(saved_uid, -1); @@ -3361,6 +3362,14 @@ static int exec_child( } } + if (context->coredump_filter_set) { + r = set_coredump_filter(context->coredump_filter); + if (ERRNO_IS_PRIVILEGE(r)) + log_unit_debug_errno(unit, r, "Failed to adjust coredump_filter, ignoring: %m"); + else if (r < 0) + return log_unit_error_errno(unit, r, "Failed to adjust coredump_filter: %m"); + } + if (context->nice_set) { r = setpriority_closest(context->nice); if (r < 0) @@ -4198,6 +4207,13 @@ void exec_context_done(ExecContext *c) { c->working_directory = mfree(c->working_directory); c->root_directory = mfree(c->root_directory); c->root_image = mfree(c->root_image); + c->root_hash = mfree(c->root_hash); + c->root_hash_size = 0; + c->root_hash_path = mfree(c->root_hash_path); + c->root_hash_sig = mfree(c->root_hash_sig); + c->root_hash_sig_size = 0; + c->root_hash_sig_path = mfree(c->root_hash_sig_path); + c->root_verity = mfree(c->root_verity); c->tty_path = mfree(c->tty_path); c->syslog_identifier = mfree(c->syslog_identifier); c->user = mfree(c->user); @@ -4602,6 +4618,30 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { if (c->root_image) fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); + if (c->root_hash) { + _cleanup_free_ char *encoded = NULL; + encoded = hexmem(c->root_hash, c->root_hash_size); + if (encoded) + fprintf(f, "%sRootHash: %s\n", prefix, encoded); + } + + if (c->root_hash_path) + fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path); + + if (c->root_hash_sig) { + _cleanup_free_ char *encoded = NULL; + ssize_t len; + len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded); + if (len) + fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded); + } + + if (c->root_hash_sig_path) + fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path); + + if (c->root_verity) + fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity); + STRV_FOREACH(e, c->environment) fprintf(f, "%sEnvironment: %s\n", prefix, *e); @@ -4637,6 +4677,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust); + if (c->coredump_filter_set) + fprintf(f, + "%sCoredumpFilter: 0x%"PRIx64"\n", + prefix, c->coredump_filter); + for (i = 0; i < RLIM_NLIMITS; i++) if (c->rlimit[i]) { fprintf(f, "%sLimit%s: " RLIM_FMT "\n", @@ -4725,17 +4770,13 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->tty_vt_disallocate)); if (IN_SET(c->std_output, - EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_KMSG, EXEC_OUTPUT_JOURNAL, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, EXEC_OUTPUT_KMSG_AND_CONSOLE, EXEC_OUTPUT_JOURNAL_AND_CONSOLE) || IN_SET(c->std_error, - EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_KMSG, EXEC_OUTPUT_JOURNAL, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, EXEC_OUTPUT_KMSG_AND_CONSOLE, EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) { @@ -4901,7 +4942,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { "%sSystemCallFilter: ", prefix); - if (!c->syscall_whitelist) + if (!c->syscall_allow_list) fputc('~', f); #if HAVE_SECCOMP @@ -5314,28 +5355,25 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) { (void) hashmap_remove(rt->manager->exec_runtime_by_id, rt->id); /* When destroy is true, then rm_rf tmp_dir and var_tmp_dir. */ - if (destroy && rt->tmp_dir) { + + if (destroy && rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) { log_debug("Spawning thread to nuke %s", rt->tmp_dir); r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir); - if (r < 0) { + if (r < 0) log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir); - free(rt->tmp_dir); - } - - rt->tmp_dir = NULL; + else + rt->tmp_dir = NULL; } - if (destroy && rt->var_tmp_dir) { + if (destroy && rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) { log_debug("Spawning thread to nuke %s", rt->var_tmp_dir); r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir); - if (r < 0) { + if (r < 0) log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir); - free(rt->var_tmp_dir); - } - - rt->var_tmp_dir = NULL; + else + rt->var_tmp_dir = NULL; } rt->id = mfree(rt->id); @@ -5349,16 +5387,22 @@ static void exec_runtime_freep(ExecRuntime **rt) { (void) exec_runtime_free(*rt, false); } -static int exec_runtime_allocate(ExecRuntime **ret) { +static int exec_runtime_allocate(ExecRuntime **ret, const char *id) { + _cleanup_free_ char *id_copy = NULL; ExecRuntime *n; assert(ret); + id_copy = strdup(id); + if (!id_copy) + return -ENOMEM; + n = new(ExecRuntime, 1); if (!n) return -ENOMEM; *n = (ExecRuntime) { + .id = TAKE_PTR(id_copy), .netns_storage_socket = { -1, -1 }, }; @@ -5369,9 +5413,9 @@ static int exec_runtime_allocate(ExecRuntime **ret) { static int exec_runtime_add( Manager *m, const char *id, - const char *tmp_dir, - const char *var_tmp_dir, - const int netns_storage_socket[2], + char **tmp_dir, + char **var_tmp_dir, + int netns_storage_socket[2], ExecRuntime **ret) { _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL; @@ -5380,51 +5424,40 @@ static int exec_runtime_add( assert(m); assert(id); + /* tmp_dir, var_tmp_dir, netns_storage_socket fds are donated on success */ + r = hashmap_ensure_allocated(&m->exec_runtime_by_id, &string_hash_ops); if (r < 0) return r; - r = exec_runtime_allocate(&rt); + r = exec_runtime_allocate(&rt, id); if (r < 0) return r; - rt->id = strdup(id); - if (!rt->id) - return -ENOMEM; - - if (tmp_dir) { - rt->tmp_dir = strdup(tmp_dir); - if (!rt->tmp_dir) - return -ENOMEM; - - /* When tmp_dir is set, then we require var_tmp_dir is also set. */ - assert(var_tmp_dir); - rt->var_tmp_dir = strdup(var_tmp_dir); - if (!rt->var_tmp_dir) - return -ENOMEM; - } - - if (netns_storage_socket) { - rt->netns_storage_socket[0] = netns_storage_socket[0]; - rt->netns_storage_socket[1] = netns_storage_socket[1]; - } - r = hashmap_put(m->exec_runtime_by_id, rt->id, rt); if (r < 0) return r; + assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */ + rt->tmp_dir = TAKE_PTR(*tmp_dir); + rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir); + + if (netns_storage_socket) { + rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]); + rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]); + } + rt->manager = m; if (ret) *ret = rt; - /* do not remove created ExecRuntime object when the operation succeeds. */ - rt = NULL; + TAKE_PTR(rt); return 0; } static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, ExecRuntime **ret) { - _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL; + _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL; _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }; int r; @@ -5436,7 +5469,10 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E if (!c->private_network && !c->private_tmp && !c->network_namespace_path) return 0; - if (c->private_tmp) { + if (c->private_tmp && + !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") && + (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") || + prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) { r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir); if (r < 0) return r; @@ -5447,12 +5483,10 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E return -errno; } - r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, netns_storage_socket, ret); + r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ret); if (r < 0) return r; - /* Avoid cleanup */ - netns_storage_socket[0] = netns_storage_socket[1] = -1; return 1; } @@ -5570,14 +5604,10 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, rt = hashmap_get(u->manager->exec_runtime_by_id, u->id); if (!rt) { - r = exec_runtime_allocate(&rt_create); + r = exec_runtime_allocate(&rt_create, u->id); if (r < 0) return log_oom(); - rt_create->id = strdup(u->id); - if (!rt_create->id) - return log_oom(); - rt = rt_create; } @@ -5634,15 +5664,16 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, rt_create->manager = u->manager; /* Avoid cleanup */ - rt_create = NULL; + TAKE_PTR(rt_create); } return 1; } -void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { - char *id = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL; - int r, fd0 = -1, fd1 = -1; +int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { + _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL; + char *id = NULL; + int r, fdpair[] = {-1, -1}; const char *p, *v = value; size_t n; @@ -5659,7 +5690,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { v = startswith(p, "tmp-dir="); if (v) { n = strcspn(v, " "); - tmp_dir = strndupa(v, n); + tmp_dir = strndup(v, n); + if (!tmp_dir) + return log_oom(); if (v[n] != ' ') goto finalize; p = v + n + 1; @@ -5668,7 +5701,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { v = startswith(p, "var-tmp-dir="); if (v) { n = strcspn(v, " "); - var_tmp_dir = strndupa(v, n); + var_tmp_dir = strndup(v, n); + if (!var_tmp_dir) + return log_oom(); if (v[n] != ' ') goto finalize; p = v + n + 1; @@ -5680,11 +5715,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { n = strcspn(v, " "); buf = strndupa(v, n); - if (safe_atoi(buf, &fd0) < 0 || !fdset_contains(fds, fd0)) { - log_debug("Unable to process exec-runtime netns fd specification."); - return; - } - fd0 = fdset_remove(fds, fd0); + if (safe_atoi(buf, &fdpair[0]) < 0 || !fdset_contains(fds, fdpair[0])) + return log_debug("Unable to process exec-runtime netns fd specification."); + fdpair[0] = fdset_remove(fds, fdpair[0]); if (v[n] != ' ') goto finalize; p = v + n + 1; @@ -5696,18 +5729,16 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) { n = strcspn(v, " "); buf = strndupa(v, n); - if (safe_atoi(buf, &fd1) < 0 || !fdset_contains(fds, fd1)) { - log_debug("Unable to process exec-runtime netns fd specification."); - return; - } - fd1 = fdset_remove(fds, fd1); + if (safe_atoi(buf, &fdpair[1]) < 0 || !fdset_contains(fds, fdpair[1])) + return log_debug("Unable to process exec-runtime netns fd specification."); + fdpair[1] = fdset_remove(fds, fdpair[1]); } finalize: - - r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, (int[]) { fd0, fd1 }, NULL); + r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, fdpair, NULL); if (r < 0) - log_debug_errno(r, "Failed to add exec-runtime: %m"); + return log_debug_errno(r, "Failed to add exec-runtime: %m"); + return 0; } void exec_runtime_vacuum(Manager *m) { @@ -5730,7 +5761,10 @@ void exec_params_clear(ExecParameters *p) { if (!p) return; - strv_free(p->environment); + p->environment = strv_free(p->environment); + p->fd_names = strv_free(p->fd_names); + p->fds = mfree(p->fds); + p->exec_fd = safe_close(p->exec_fd); } static const char* const exec_input_table[_EXEC_INPUT_MAX] = { @@ -5750,8 +5784,6 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { [EXEC_OUTPUT_INHERIT] = "inherit", [EXEC_OUTPUT_NULL] = "null", [EXEC_OUTPUT_TTY] = "tty", - [EXEC_OUTPUT_SYSLOG] = "syslog", - [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console", [EXEC_OUTPUT_KMSG] = "kmsg", [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", [EXEC_OUTPUT_JOURNAL] = "journal", diff --git a/src/core/execute.h b/src/core/execute.h index 4baf5b1a4..fc7bc5c24 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -14,6 +14,7 @@ typedef struct Manager Manager; #include #include "cgroup-util.h" +#include "coredump-util.h" #include "cpu-set-util.h" #include "exec-util.h" #include "fdset.h" @@ -51,8 +52,6 @@ typedef enum ExecOutput { EXEC_OUTPUT_INHERIT, EXEC_OUTPUT_NULL, EXEC_OUTPUT_TTY, - EXEC_OUTPUT_SYSLOG, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE, EXEC_OUTPUT_JOURNAL, @@ -156,11 +155,14 @@ struct ExecContext { char **unset_environment; struct rlimit *rlimit[_RLIMIT_MAX]; - char *working_directory, *root_directory, *root_image; + char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path; + void *root_hash, *root_hash_sig; + size_t root_hash_size, root_hash_sig_size; bool working_directory_missing_ok:1; bool working_directory_home:1; bool oom_score_adjust_set:1; + bool coredump_filter_set:1; bool nice_set:1; bool ioprio_set:1; bool cpu_sched_set:1; @@ -179,6 +181,7 @@ struct ExecContext { int ioprio; int cpu_sched_policy; int cpu_sched_priority; + uint64_t coredump_filter; CPUSet cpu_set; NUMAPolicy numa_policy; @@ -284,9 +287,9 @@ struct ExecContext { Hashmap *syscall_filter; Set *syscall_archs; int syscall_errno; - bool syscall_whitelist:1; + bool syscall_allow_list:1; - bool address_families_whitelist:1; + bool address_families_allow_list:1; Set *address_families; char *network_namespace_path; @@ -402,7 +405,7 @@ ExecRuntime *exec_runtime_unref(ExecRuntime *r, bool destroy); int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds); int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds); -void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds); +int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds); void exec_runtime_vacuum(Manager *m); void exec_params_clear(ExecParameters *p); diff --git a/src/core/generator-setup.c b/src/core/generator-setup.c new file mode 100644 index 000000000..78ff59090 --- /dev/null +++ b/src/core/generator-setup.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "generator-setup.h" +#include "macro.h" +#include "mkdir.h" +#include "rm-rf.h" + +int lookup_paths_mkdir_generator(LookupPaths *p) { + int r, q; + + assert(p); + + if (!p->generator || !p->generator_early || !p->generator_late) + return -EINVAL; + + r = mkdir_p_label(p->generator, 0755); + + q = mkdir_p_label(p->generator_early, 0755); + if (q < 0 && r >= 0) + r = q; + + q = mkdir_p_label(p->generator_late, 0755); + if (q < 0 && r >= 0) + r = q; + + return r; +} + +void lookup_paths_trim_generator(LookupPaths *p) { + assert(p); + + /* Trim empty dirs */ + + if (p->generator) + (void) rmdir(p->generator); + if (p->generator_early) + (void) rmdir(p->generator_early); + if (p->generator_late) + (void) rmdir(p->generator_late); +} + +void lookup_paths_flush_generator(LookupPaths *p) { + assert(p); + + /* Flush the generated unit files in full */ + + if (p->generator) + (void) rm_rf(p->generator, REMOVE_ROOT|REMOVE_PHYSICAL); + if (p->generator_early) + (void) rm_rf(p->generator_early, REMOVE_ROOT|REMOVE_PHYSICAL); + if (p->generator_late) + (void) rm_rf(p->generator_late, REMOVE_ROOT|REMOVE_PHYSICAL); + + if (p->temporary_dir) + (void) rm_rf(p->temporary_dir, REMOVE_ROOT|REMOVE_PHYSICAL); +} diff --git a/src/core/generator-setup.h b/src/core/generator-setup.h new file mode 100644 index 000000000..9688601a7 --- /dev/null +++ b/src/core/generator-setup.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "path-lookup.h" + +int lookup_paths_mkdir_generator(LookupPaths *p); +void lookup_paths_trim_generator(LookupPaths *p); +void lookup_paths_flush_generator(LookupPaths *p); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 83cce8813..6d047db83 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -10,29 +10,41 @@ #include "hostname-util.h" #include "log.h" #include "macro.h" +#include "proc-cmdline.h" #include "string-util.h" #include "util.h" int hostname_setup(void) { _cleanup_free_ char *b = NULL; + const char *hn = NULL; bool enoent = false; - const char *hn; int r; - r = read_etc_hostname(NULL, &b); - if (r < 0) { - if (r == -ENOENT) - enoent = true; - else - log_warning_errno(r, "Failed to read configured hostname: %m"); + r = proc_cmdline_get_key("systemd.hostname", 0, &b); + if (r < 0) + log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m"); + else if (r > 0) { + if (hostname_is_valid(b, true)) + hn = b; + else { + log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b); + b = mfree(b); + } + } - hn = NULL; - } else - hn = b; + if (!hn) { + r = read_etc_hostname(NULL, &b); + if (r < 0) { + if (r == -ENOENT) + enoent = true; + else + log_warning_errno(r, "Failed to read configured hostname: %m"); + } else + hn = b; + } if (isempty(hn)) { - /* Don't override the hostname if it is already set - * and not explicitly configured */ + /* Don't override the hostname if it is already set and not explicitly configured */ if (hostname_is_set()) return 0; diff --git a/src/core/job.c b/src/core/job.c index 4f9336fcd..d97cb64d3 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -263,6 +263,10 @@ int job_install_deserialized(Job *j) { return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EEXIST), "Unit already has a job installed. Not installing deserialized job."); + r = hashmap_ensure_allocated(&j->manager->jobs, NULL); + if (r < 0) + return r; + r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j); if (r == -EEXIST) return log_unit_debug_errno(j->unit, r, "Job ID %" PRIu32 " already used, cannot deserialize job.", j->id); @@ -986,9 +990,10 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr j->result = result; - log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s", j->id, u->id, job_type_to_string(t), job_result_to_string(result)); + log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s", + j->id, u->id, job_type_to_string(t), job_result_to_string(result)); - /* If this job did nothing to respective unit we don't log the status message */ + /* If this job did nothing to the respective unit we don't log the status message */ if (!already) job_emit_done_status_message(u, j->id, t, result); @@ -1025,7 +1030,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr * aren't active. This is when the verify-active job merges with a * satisfying job type, and then loses it's invalidation effect, as the * result there is JOB_DONE for the start job we merged into, while we - * should be failing the depending job if the said unit isn't infact + * should be failing the depending job if the said unit isn't in fact * active. Oneshots are an example of this, where going directly from * activating to inactive is success. * diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index f61e9da6f..fb3f68561 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -19,9 +19,8 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff r = unit_file_find_dropin_paths(NULL, u->manager->lookup_paths.search_path, u->manager->unit_path_cache, - dir_suffix, - NULL, - u->names, + dir_suffix, NULL, + u->id, u->aliases, &paths); if (r < 0) return r; @@ -114,12 +113,13 @@ int unit_load_dropin(Unit *u) { } STRV_FOREACH(f, u->dropin_paths) - (void) config_parse(u->id, *f, NULL, - UNIT_VTABLE(u)->sections, - config_item_perf_lookup, load_fragment_gperf_lookup, - 0, u); - - u->dropin_mtime = now(CLOCK_REALTIME); + (void) config_parse( + u->id, *f, NULL, + UNIT_VTABLE(u)->sections, + config_item_perf_lookup, load_fragment_gperf_lookup, + 0, + u, + &u->dropin_mtime); return 0; } diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index ea15554d8..5e2ec0d80 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -13,7 +13,7 @@ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { u->manager->lookup_paths.search_path, u->manager->unit_path_cache, ".d", ".conf", - u->names, + u->id, u->aliases, paths); } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 69abdb65b..12ae78eb7 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -23,11 +23,15 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) $1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory) $1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image) +$1.RootHash, config_parse_exec_root_hash, 0, offsetof($1, exec_context) +$1.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof($1, exec_context) +$1.RootVerity, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_verity) $1.User, config_parse_user_group_compat, 0, offsetof($1, exec_context.user) $1.Group, config_parse_user_group_compat, 0, offsetof($1, exec_context.group) $1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups) $1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) $1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) +$1.CoredumpFilter, config_parse_exec_coredump_filter, 0, offsetof($1, exec_context) $1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) $1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context) $1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context) @@ -321,6 +325,8 @@ Service.TimeoutSec, config_parse_service_timeout, 0, Service.TimeoutStartSec, config_parse_service_timeout, 0, 0 Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec) Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0 +Service.TimeoutStartFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_start_failure_mode) +Service.TimeoutStopFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_stop_failure_mode) Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) m4_dnl The following five only exist for compatibility, they moved into Unit, see above @@ -395,6 +401,7 @@ Socket.Transparent, config_parse_bool, 0, Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) +Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo) Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) @@ -428,6 +435,7 @@ Mount.DirectoryMode, config_parse_mode, 0, Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options) Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount) Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount) +Mount.ReadWriteOnly, config_parse_bool, 0, offsetof(Mount, read_write_only) EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 01448fa88..3036aa8ba 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -13,6 +13,8 @@ #include #include +#include "sd-messages.h" + #include "af-list.h" #include "alloc-util.h" #include "all-units.h" @@ -29,6 +31,7 @@ #include "errno-list.h" #include "escape.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "hexdecoct.h" #include "io-util.h" @@ -117,13 +120,13 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGrou DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value"); DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value"); DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value"); DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy"); DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value"); @@ -592,6 +595,87 @@ int config_parse_exec_oom_score_adjust( return 0; } +int config_parse_exec_coredump_filter( + 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) { + + ExecContext *c = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + c->coredump_filter = 0; + c->coredump_filter_set = false; + return 0; + } + + uint64_t f; + r = coredump_filter_mask_from_string(rvalue, &f); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue); + return 0; + } + + c->coredump_filter |= f; + c->oom_score_adjust_set = true; + return 0; +} + +int config_parse_kill_mode( + 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) { + + KillMode *k = data, m; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *k = KILL_CONTROL_GROUP; + return 0; + } + + m = kill_mode_from_string(rvalue); + if (m < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse kill mode specification, ignoring: %s", rvalue); + return 0; + } + + if (m == KILL_NONE) + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Unit configured to use KillMode=none. " + "This is unsafe, as it disables systemd's process lifecycle management for the service. " + "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. " + "Support for KillMode=none is deprecated and will eventually be removed."); + + *k = m; + return 0; +} + int config_parse_exec( const char *unit, const char *filename, @@ -1060,6 +1144,7 @@ int config_parse_exec_output( const char *n; ExecContext *c = data; const Unit *u = userdata; + bool obsolete = false; ExecOutput eo; int r; @@ -1084,6 +1169,14 @@ int config_parse_exec_output( eo = EXEC_OUTPUT_NAMED_FD; + } else if (streq(rvalue, "syslog")) { + eo = EXEC_OUTPUT_JOURNAL; + obsolete = true; + + } else if (streq(rvalue, "syslog+console")) { + eo = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + obsolete = true; + } else if ((n = startswith(rvalue, "file:"))) { r = unit_full_printf(u, n, &resolved); @@ -1115,6 +1208,11 @@ int config_parse_exec_output( } } + if (obsolete) + log_syntax(unit, LOG_NOTICE, filename, line, 0, + "Standard output type %s is obsolete, automatically updating to %s. Please update your unit file, and consider removing the setting altogether.", + rvalue, exec_output_to_string(eo)); + if (streq(lvalue, "StandardOutput")) { if (eo == EXEC_OUTPUT_NAMED_FD) free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved); @@ -1318,6 +1416,124 @@ int config_parse_exec_cpu_sched_prio(const char *unit, return 0; } +int config_parse_exec_root_hash( + 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_ void *roothash_decoded = NULL; + ExecContext *c = data; + size_t roothash_decoded_size = 0; + int r; + + assert(data); + assert(filename); + assert(line); + assert(rvalue); + + if (isempty(rvalue)) { + /* Reset if the empty string is assigned */ + c->root_hash_path = mfree(c->root_hash_path); + c->root_hash = mfree(c->root_hash); + c->root_hash_size = 0; + return 0; + } + + if (path_is_absolute(rvalue)) { + /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */ + _cleanup_free_ char *p = NULL; + + p = strdup(rvalue); + if (!p) + return -ENOMEM; + + free_and_replace(c->root_hash_path, p); + c->root_hash = mfree(c->root_hash); + c->root_hash_size = 0; + return 0; + } + + /* We have a roothash to decode, eg: RootHash=012345789abcdef */ + r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue); + if (roothash_decoded_size < sizeof(sd_id128_t)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "RootHash= is too short, ignoring: %s", rvalue); + + free_and_replace(c->root_hash, roothash_decoded); + c->root_hash_size = roothash_decoded_size; + c->root_hash_path = mfree(c->root_hash_path); + + return 0; +} + +int config_parse_exec_root_hash_sig( + 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_ void *roothash_sig_decoded = NULL; + char *value; + ExecContext *c = data; + size_t roothash_sig_decoded_size = 0; + int r; + + assert(data); + assert(filename); + assert(line); + assert(rvalue); + + if (isempty(rvalue)) { + /* Reset if the empty string is assigned */ + c->root_hash_sig_path = mfree(c->root_hash_sig_path); + c->root_hash_sig = mfree(c->root_hash_sig); + c->root_hash_sig_size = 0; + return 0; + } + + if (path_is_absolute(rvalue)) { + /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */ + _cleanup_free_ char *p = NULL; + + p = strdup(rvalue); + if (!p) + return -ENOMEM; + + free_and_replace(c->root_hash_sig_path, p); + c->root_hash_sig = mfree(c->root_hash_sig); + c->root_hash_sig_size = 0; + return 0; + } + + if (!(value = startswith(rvalue, "base64:"))) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue); + + /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */ + r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue); + + free_and_replace(c->root_hash_sig, roothash_sig_decoded); + c->root_hash_sig_size = roothash_sig_decoded_size; + c->root_hash_sig_path = mfree(c->root_hash_sig_path); + + return 0; +} + int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, @@ -1895,7 +2111,7 @@ int config_parse_bus_name( return 0; } - if (!service_name_is_valid(k)) { + if (!sd_bus_service_name_is_valid(k)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name, ignoring: %s", k); return 0; } @@ -2073,6 +2289,15 @@ int config_parse_user_group_compat( return -ENOEXEC; } + if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME)) + log_struct(LOG_NOTICE, + "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k, + "UNIT=%s", unit, + "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR, + "OFFENDING_USER=%s", k, + "CONFIG_FILE=%s", filename, + "CONFIG_LINE=%u", line); + return free_and_replace(*user, k); } @@ -2863,7 +3088,7 @@ int config_parse_syscall_filter( if (isempty(rvalue)) { /* Empty assignment resets the list */ c->syscall_filter = hashmap_free(c->syscall_filter); - c->syscall_whitelist = false; + c->syscall_allow_list = false; return 0; } @@ -2879,15 +3104,15 @@ int config_parse_syscall_filter( if (invert) /* Allow everything but the ones listed */ - c->syscall_whitelist = false; + c->syscall_allow_list = false; else { /* Allow nothing but the ones listed */ - c->syscall_whitelist = true; + c->syscall_allow_list = true; - /* Accept default syscalls if we are on a whitelist */ + /* Accept default syscalls if we are on a allow_list */ r = seccomp_parse_syscall_filter( "@default", -1, c->syscall_filter, - SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_WHITELIST, + SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST, unit, NULL, 0); if (r < 0) @@ -2920,7 +3145,7 @@ int config_parse_syscall_filter( name, num, c->syscall_filter, SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE| (invert ? SECCOMP_PARSE_INVERT : 0)| - (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0), + (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0), unit, filename, line); if (r < 0) return r; @@ -2948,10 +3173,6 @@ int config_parse_syscall_archs( return 0; } - r = set_ensure_allocated(archs, NULL); - if (r < 0) - return log_oom(); - for (;;) { _cleanup_free_ char *word = NULL; uint32_t a; @@ -2974,7 +3195,7 @@ int config_parse_syscall_archs( continue; } - r = set_put(*archs, UINT32_TO_PTR(a + 1)); + r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1)); if (r < 0) return log_oom(); } @@ -3039,7 +3260,7 @@ int config_parse_address_families( if (isempty(rvalue)) { /* Empty assignment resets the list */ c->address_families = set_free(c->address_families); - c->address_families_whitelist = false; + c->address_families_allow_list = false; return 0; } @@ -3053,7 +3274,7 @@ int config_parse_address_families( if (!c->address_families) return log_oom(); - c->address_families_whitelist = !invert; + c->address_families_allow_list = !invert; } for (p = rvalue;;) { @@ -3081,7 +3302,7 @@ int config_parse_address_families( /* If we previously wanted to forbid an address family and now * we want to allow it, then just remove it from the list. */ - if (!invert == c->address_families_whitelist) { + if (!invert == c->address_families_allow_list) { r = set_put(c->address_families, INT_TO_PTR(af)); if (r < 0) return log_oom(); @@ -3277,7 +3498,12 @@ int config_parse_memory_limit( uint64_t bytes = CGROUP_LIMIT_MAX; int r; - if (!isempty(rvalue) && !streq(rvalue, "infinity")) { + if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow", + "DefaultMemoryMin", + "MemoryLow", + "MemoryMin")) + bytes = CGROUP_LIMIT_MIN; + else if (!isempty(rvalue) && !streq(rvalue, "infinity")) { r = parse_permille(rvalue); if (r < 0) { @@ -3297,17 +3523,11 @@ int config_parse_memory_limit( } if (streq(lvalue, "DefaultMemoryLow")) { + c->default_memory_low = bytes; c->default_memory_low_set = true; - if (isempty(rvalue)) - c->default_memory_low = CGROUP_LIMIT_MIN; - else - c->default_memory_low = bytes; } else if (streq(lvalue, "DefaultMemoryMin")) { + c->default_memory_min = bytes; c->default_memory_min_set = true; - if (isempty(rvalue)) - c->default_memory_min = CGROUP_LIMIT_MIN; - else - c->default_memory_min = bytes; } else if (streq(lvalue, "MemoryMin")) { c->memory_min = bytes; c->memory_min_set = true; @@ -4770,7 +4990,9 @@ int unit_load_fragment(Unit *u) { r = config_parse(u->id, fragment, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, load_fragment_gperf_lookup, - CONFIG_PARSE_ALLOW_INCLUDE, u); + 0, + u, + NULL); if (r == -ENOEXEC) log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started."); if (r < 0) @@ -4845,6 +5067,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_exec, "PATH [ARGUMENT [...]]" }, { config_parse_service_type, "SERVICETYPE" }, { config_parse_service_restart, "SERVICERESTART" }, + { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" }, { config_parse_kill_mode, "KILLMODE" }, { config_parse_signal, "SIGNAL" }, { config_parse_socket_listen, "SOCKET [...]" }, @@ -5005,22 +5228,36 @@ int config_parse_output_restricted( void *userdata) { ExecOutput t, *eo = data; + bool obsolete = false; assert(filename); assert(lvalue); assert(rvalue); assert(data); - t = exec_output_from_string(rvalue); - if (t < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue); - return 0; + if (streq(rvalue, "syslog")) { + t = EXEC_OUTPUT_JOURNAL; + obsolete = true; + } else if (streq(rvalue, "syslog+console")) { + t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + obsolete = true; + } else { + t = exec_output_from_string(rvalue); + if (t < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue); + return 0; + } + + if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue); + return 0; + } } - if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue); - return 0; - } + if (obsolete) + log_syntax(unit, LOG_NOTICE, filename, line, 0, + "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.", + rvalue, exec_output_to_string(t)); *eo = t; return 0; diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index b6b46b244..ac3940a1b 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -26,9 +26,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_socket_protocol); CONFIG_PARSER_PROTOTYPE(config_parse_socket_bind); CONFIG_PARSER_PROTOTYPE(config_parse_exec_nice); CONFIG_PARSER_PROTOTYPE(config_parse_exec_oom_score_adjust); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_coredump_filter); CONFIG_PARSER_PROTOTYPE(config_parse_exec); CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout); CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort); +CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode); CONFIG_PARSER_PROTOTYPE(config_parse_service_type); CONFIG_PARSER_PROTOTYPE(config_parse_service_restart); CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice); @@ -42,6 +44,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy); CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio); CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity); CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash); +CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig); CONFIG_PARSER_PROTOTYPE(config_parse_capability_set); CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags); CONFIG_PARSER_PROTOTYPE(config_parse_timer); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 284b77c1f..f76b82a8a 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -223,11 +223,9 @@ int machine_id_commit(const char *root) { return log_error_errno(r, "Can't fetch current mount namespace: %m"); /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */ - if (unshare(CLONE_NEWNS) < 0) - return log_error_errno(errno, "Failed to enter new namespace: %m"); - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) - return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m"); + r = detach_mount_namespace(); + if (r < 0) + return log_error_errno(r, "Failed to set up new mount namespace: %m"); if (umount(etc_machine_id) < 0) return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id); diff --git a/src/core/main.c b/src/core/main.c index 380a80944..4a376976e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -19,6 +19,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "apparmor-setup.h" #include "architecture.h" #include "build.h" #include "bus-error.h" @@ -41,6 +42,7 @@ #include "fileio.h" #include "format-util.h" #include "fs-util.h" +#include "hexdecoct.h" #include "hostname-setup.h" #include "ima-setup.h" #include "killall.h" @@ -59,6 +61,7 @@ #include "pretty-print.h" #include "proc-cmdline.h" #include "process-util.h" +#include "random-util.h" #include "raw-clone.h" #include "rlimit-util.h" #if HAVE_SECCOMP @@ -94,10 +97,13 @@ static enum { ACTION_TEST, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES, + ACTION_BUS_INTROSPECT, } arg_action = ACTION_RUN; -/* Those variables are initalized to 0 automatically, so we avoid uninitialized memory access. - * Real defaults are assigned in reset_arguments() below. */ +static const char *arg_bus_introspect = NULL; + +/* Those variables are initialized to 0 automatically, so we avoid uninitialized memory access. Real + * defaults are assigned in reset_arguments() below. */ static char *arg_default_unit; static bool arg_system; static bool arg_dump_core; @@ -144,6 +150,9 @@ static EmergencyAction arg_cad_burst_action; static OOMPolicy arg_default_oom_policy; static CPUSet arg_cpu_affinity; static NUMAPolicy arg_numa_policy; +static usec_t arg_clock_usec; +static void *arg_random_seed; +static size_t arg_random_seed_size; /* A copy of the original environment block */ static char **saved_env = NULL; @@ -319,7 +328,6 @@ static int set_machine_id(const char *m) { } static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r; assert(key); @@ -331,10 +339,8 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value); - else if (in_initrd() == !!startswith(key, "rd.")) { - if (free_and_strdup(&arg_default_unit, value) < 0) - return log_oom(); - } + else if (in_initrd() == !!startswith(key, "rd.")) + return free_and_strdup_warn(&arg_default_unit, value); } else if (proc_cmdline_key_streq(key, "systemd.dump_core")) { @@ -483,7 +489,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = parse_cpu_set(value, &arg_cpu_affinity); if (r < 0) - log_warning_errno(r, "Faile to parse CPU affinity mask '%s', ignoring: %m", value); + log_warning_errno(r, "Failed to parse CPU affinity mask '%s', ignoring: %m", value); } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) { @@ -492,6 +498,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat (void) parse_path_argument_and_warn(value, false, &arg_watchdog_device); + } else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = safe_atou64(value, &arg_clock_usec); + if (r < 0) + log_warning_errno(r, "Failed to parse systemd.clock_usec= argument, ignoring: %s", value); + + } else if (proc_cmdline_key_streq(key, "systemd.random_seed")) { + void *p; + size_t sz; + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = unbase64mem(value, (size_t) -1, &p, &sz); + if (r < 0) + log_warning_errno(r, "Failed to parse systemd.random_seed= argument, ignoring: %s", value); + + free(arg_random_seed); + arg_random_seed = sz > 0 ? p : mfree(p); + arg_random_seed_size = sz; + } else if (streq(key, "quiet") && !value) { if (arg_show_status == _SHOW_STATUS_INVALID) @@ -508,10 +538,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } else if (!value) { const char *target; - /* SysV compatibility */ + /* Compatible with SysV, but supported independently even if SysV compatibility is disabled. */ target = runlevel_to_target(key); if (target) - return free_and_strdup(&arg_default_unit, target); + return free_and_strdup_warn(&arg_default_unit, target); } return 0; @@ -546,8 +576,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level"); DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target"); -DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ); +DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color"); DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location"); +DEFINE_SETTER(config_parse_time, log_show_time_from_string, "time"); static int config_parse_default_timeout_abort( const char *unit, @@ -575,6 +606,7 @@ static int parse_config_file(void) { { "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 }, @@ -646,7 +678,13 @@ static int parse_config_file(void) { CONF_PATHS_NULSTR("systemd/system.conf.d") : CONF_PATHS_NULSTR("systemd/user.conf.d"); - (void) config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, CONFIG_PARSE_WARN, NULL); + (void) config_parse_many_nulstr( + fn, conf_dirs_nulstr, + "Manager\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY * like everywhere else. */ @@ -702,16 +740,18 @@ static void set_manager_settings(Manager *m) { assert(m); - /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager - * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */ + /* Propagates the various manager settings into the manager object, i.e. properties that + * effect the manager itself (as opposed to just being inherited into newly allocated + * units, see set_manager_defaults() above). */ m->confirm_spawn = arg_confirm_spawn; m->service_watchdogs = arg_service_watchdogs; - m->runtime_watchdog = arg_runtime_watchdog; - m->reboot_watchdog = arg_reboot_watchdog; - m->kexec_watchdog = arg_kexec_watchdog; m->cad_burst_action = arg_cad_burst_action; + 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_show_status(m, arg_show_status, "commandline"); m->status_unit_format = arg_status_unit_format; } @@ -722,6 +762,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LOG_TARGET, ARG_LOG_COLOR, ARG_LOG_LOCATION, + ARG_LOG_TIME, ARG_UNIT, ARG_SYSTEM, ARG_USER, @@ -730,6 +771,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION, ARG_DUMP_CONFIGURATION_ITEMS, ARG_DUMP_BUS_PROPERTIES, + ARG_BUS_INTROSPECT, ARG_DUMP_CORE, ARG_CRASH_CHVT, ARG_CRASH_SHELL, @@ -749,6 +791,7 @@ static int parse_argv(int argc, char *argv[]) { { "log-target", required_argument, NULL, ARG_LOG_TARGET }, { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "log-time", optional_argument, NULL, ARG_LOG_TIME }, { "unit", required_argument, NULL, ARG_UNIT }, { "system", no_argument, NULL, ARG_SYSTEM }, { "user", no_argument, NULL, ARG_USER }, @@ -758,6 +801,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, + { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, @@ -822,6 +866,18 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_LOG_TIME: + + if (optarg) { + r = log_show_time_from_string(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse log time setting \"%s\": %m", + optarg); + } else + log_show_time(true); + + break; + case ARG_DEFAULT_STD_OUTPUT: r = exec_output_from_string(optarg); if (r < 0) @@ -873,6 +929,11 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_DUMP_BUS_PROPERTIES; break; + case ARG_BUS_INTROSPECT: + arg_bus_introspect = optarg; + arg_action = ACTION_BUS_INTROSPECT; + break; + case ARG_DUMP_CORE: if (!optarg) arg_dump_core = true; @@ -988,11 +1049,9 @@ static int parse_argv(int argc, char *argv[]) { case 'b': case 's': case 'z': - /* Just to eat away the sysvinit kernel - * cmdline args without getopt() error - * messages that we'll parse in - * parse_proc_cmdline_word() or ignore. */ - + /* Just to eat away the sysvinit kernel cmdline args that we'll parse in + * parse_proc_cmdline_item() or ignore, without any getopt() error messages. + */ case '?': if (getpid_cached() != 1) return -EINVAL; @@ -1023,7 +1082,9 @@ static int help(void) { return log_oom(); printf("%s [OPTIONS...]\n\n" - "Starts up and maintains the system or user services.\n\n" + "%sStarts and monitors system and user services.%s\n\n" + "This program takes no positional arguments.\n\n" + "%sOptions%s:\n" " -h --help Show this help\n" " --version Show version\n" " --test Determine initial transaction, dump it and exit\n" @@ -1032,6 +1093,7 @@ static int help(void) { " --no-pager Do not pipe output into a pager\n" " --dump-configuration-items Dump understood unit configuration items\n" " --dump-bus-properties Dump exposed bus properties\n" + " --bus-introspect=PATH Write XML introspection data\n" " --unit=UNIT Set default unit\n" " --dump-core[=BOOL] Dump core on crash\n" " --crash-vt=NR Change to specified VT on crash\n" @@ -1043,10 +1105,13 @@ static int help(void) { " --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n" " --log-color[=BOOL] Highlight important log messages\n" " --log-location[=BOOL] Include code location in log messages\n" + " --log-time[=BOOL] Prefix log messages with current time\n" " --default-standard-output= Set default standard output for services\n" " --default-standard-error= Set default standard error output for services\n" "\nSee the %s for details.\n" , program_invocation_short_name + , ansi_highlight(), ansi_normal() + , ansi_underline(), ansi_normal() , link ); @@ -1209,7 +1274,7 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) { assert_cc(RLIM_INFINITY > 0); mm = physical_memory() / 8; /* Let's scale how much we allow to be locked by the amount of physical - * RAM. We allow an eigth to be locked by us, just to pick a value. */ + * RAM. We allow an eighth to be locked by us, just to pick a value. */ new_rlimit = (struct rlimit) { .rlim_cur = MAX3(HIGH_RLIMIT_MEMLOCK, saved_rlimit->rlim_cur, mm), @@ -1429,6 +1494,9 @@ static int become_shutdown( if (log_get_show_location()) command_line[pos++] = "--log-location"; + if (log_get_show_time()) + command_line[pos++] = "--log-time"; + if (streq(shutdown_verb, "exit")) { command_line[pos++] = "--exit-code"; command_line[pos++] = exit_code; @@ -1475,6 +1543,9 @@ static int become_shutdown( static void initialize_clock(void) { int r; + /* This is called very early on, before we parse the kernel command line or otherwise figure out why + * we are running, but only once. */ + if (clock_is_localtime(NULL) > 0) { int min; @@ -1513,6 +1584,62 @@ static void initialize_clock(void) { log_info("System time before build time, advancing clock."); } +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. */ + + if (arg_clock_usec == 0) + return; + + if (getpid_cached() != 1) + return; + + if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0) + log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m"); + else { + char buf[FORMAT_TIMESTAMP_MAX]; + + log_info("Set system clock to %s, as specified on the kernel command line.", + format_timestamp(buf, sizeof(buf), arg_clock_usec)); + } +} + +static void cmdline_take_random_seed(void) { + _cleanup_close_ int random_fd = -1; + size_t suggested; + int r; + + if (arg_random_seed_size == 0) + return; + + if (getpid_cached() != 1) + return; + + assert(arg_random_seed); + suggested = random_pool_size(); + + if (arg_random_seed_size < suggested) + log_warning("Random seed specified on kernel command line has size %zu, but %zu bytes required to fill entropy pool.", + arg_random_seed_size, suggested); + + random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); + if (random_fd < 0) { + log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m"); + return; + } + + r = random_write_entropy(random_fd, arg_random_seed, arg_random_seed_size, true); + if (r < 0) { + log_warning_errno(r, "Failed to credit entropy specified on kernel command line, ignoring: %m"); + return; + } + + log_notice("Successfully credited entropy passed on kernel command line.\n" + "Note that the seed provided this way is accessible to unprivileged programs. This functionality should not be used outside of testing environments."); +} + static void initialize_coredump(bool skip_setup) { #if ENABLE_COREDUMP if (getpid_cached() != 1) @@ -1752,11 +1879,10 @@ static int invoke_main_loop( saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; - mac_selinux_reload(); - (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); set_manager_defaults(m); + set_manager_settings(m); update_cpu_affinity(false); update_numa_policy(false); @@ -1947,9 +2073,6 @@ static int initialize_runtime( if (r < 0) log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device); } - - if (timestamp_is_set(arg_runtime_watchdog)) - watchdog_set_timeout(&arg_runtime_watchdog); } if (arg_timer_slack_nsec != NSEC_INFINITY) @@ -2002,21 +2125,21 @@ static int do_queue_default_job( const char **ret_error_message) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char* default_unit; - Job *default_unit_job; - Unit *target = NULL; + const char *unit; + Job *job; + Unit *target; int r; if (arg_default_unit) - default_unit = arg_default_unit; + unit = arg_default_unit; else if (in_initrd()) - default_unit = SPECIAL_INITRD_TARGET; + unit = SPECIAL_INITRD_TARGET; else - default_unit = SPECIAL_DEFAULT_TARGET; + unit = SPECIAL_DEFAULT_TARGET; - log_debug("Activating default unit: %s", default_unit); + log_debug("Activating default unit: %s", unit); - r = manager_load_startable_unit_or_warn(m, default_unit, NULL, &target); + r = manager_load_startable_unit_or_warn(m, unit, NULL, &target); if (r < 0 && in_initrd() && !arg_default_unit) { /* Fall back to default.target, which we used to always use by default. Only do this if no * explicit configuration was given. */ @@ -2038,13 +2161,13 @@ static int do_queue_default_job( assert(target->load_state == UNIT_LOADED); - r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &job); if (r == -EPERM) { log_debug_errno(r, "Default target could not be isolated, starting instead: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &job); if (r < 0) { *ret_error_message = "Failed to start default target"; return log_emergency_errno(r, "Failed to start default target: %s", bus_error_message(&error, r)); @@ -2053,9 +2176,12 @@ static int do_queue_default_job( } else if (r < 0) { *ret_error_message = "Failed to isolate default target"; return log_emergency_errno(r, "Failed to isolate default target: %s", bus_error_message(&error, r)); - } + } else + log_info("Queued %s job for default target %s.", + job_type_to_string(job->type), + unit_status_string(job->unit)); - m->default_unit_job_id = default_unit_job->id; + m->default_unit_job_id = job->id; return 0; } @@ -2191,6 +2317,10 @@ static void reset_arguments(void) { cpu_set_reset(&arg_cpu_affinity); numa_policy_reset(&arg_numa_policy); + + arg_random_seed = mfree(arg_random_seed); + arg_random_seed_size = 0; + arg_clock_usec = 0; } static int parse_configuration(const struct rlimit *saved_rlimit_nofile, @@ -2220,29 +2350,6 @@ static int parse_configuration(const struct rlimit *saved_rlimit_nofile, /* Note that this also parses bits from the kernel command line, including "debug". */ log_parse_environment(); - return 0; -} - -static int load_configuration( - int argc, - char **argv, - const struct rlimit *saved_rlimit_nofile, - const struct rlimit *saved_rlimit_memlock, - const char **ret_error_message) { - int r; - - assert(saved_rlimit_nofile); - assert(saved_rlimit_memlock); - assert(ret_error_message); - - (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); - - r = parse_argv(argc, argv); - if (r < 0) { - *ret_error_message = "Failed to parse commandline arguments"; - return r; - } - /* Initialize the show status setting if it hasn't been set explicitly yet */ if (arg_show_status == _SHOW_STATUS_INVALID) arg_show_status = SHOW_STATUS_YES; @@ -2321,6 +2428,12 @@ static int initialize_security( return r; } + r = mac_apparmor_setup(); + if (r < 0) { + *ret_error_message = "Failed to load AppArmor policy"; + return r; + } + r = ima_setup(); if (r < 0) { *ret_error_message = "Failed to load IMA policy"; @@ -2506,7 +2619,7 @@ int main(int argc, char *argv[]) { } if (mac_selinux_init() < 0) { - error_message = "Failed to initialize SELinux policy"; + error_message = "Failed to initialize SELinux support"; goto finish; } @@ -2527,8 +2640,7 @@ int main(int argc, char *argv[]) { /* For later on, see above... */ log_set_target(LOG_TARGET_JOURNAL); - /* clear the kernel timestamp, - * because we are in a container */ + /* clear the kernel timestamp, because we are in a container */ kernel_timestamp = DUAL_TIMESTAMP_NULL; } @@ -2547,9 +2659,13 @@ int main(int argc, char *argv[]) { log_set_target(LOG_TARGET_AUTO); log_open(); - /* clear the kernel timestamp, - * because we are not PID 1 */ + /* clear the kernel timestamp, because we are not PID 1 */ kernel_timestamp = DUAL_TIMESTAMP_NULL; + + if (mac_selinux_init() < 0) { + error_message = "Failed to initialize SELinux support"; + goto finish; + } } if (arg_system) { @@ -2562,15 +2678,14 @@ int main(int argc, char *argv[]) { log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m"); } - /* Mount /proc, /sys and friends, so that /proc/cmdline and - * /proc/$PID/fd is available. */ + /* Mount /proc, /sys and friends, so that /proc/cmdline and /proc/$PID/fd is available. */ if (getpid_cached() == 1) { /* Load the kernel modules early. */ if (!skip_setup) kmod_setup(); - r = mount_setup(loaded_policy); + r = mount_setup(loaded_policy, skip_setup); if (r < 0) { error_message = "Failed to mount API filesystems"; goto finish; @@ -2592,15 +2707,19 @@ int main(int argc, char *argv[]) { (void) reset_all_signal_handlers(); (void) ignore_signals(SIGNALS_IGNORE, -1); - r = load_configuration(argc, argv, &saved_rlimit_nofile, &saved_rlimit_memlock, &error_message); - if (r < 0) + (void) parse_configuration(&saved_rlimit_nofile, &saved_rlimit_memlock); + + r = parse_argv(argc, argv); + if (r < 0) { + error_message = "Failed to parse commandline arguments"; goto finish; + } r = safety_checks(); if (r < 0) goto finish; - if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES)) + if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES, ACTION_BUS_INTROSPECT)) (void) pager_open(arg_pager_flags); if (arg_action != ACTION_RUN) @@ -2620,6 +2739,10 @@ int main(int argc, char *argv[]) { dump_bus_properties(stdout); retval = EXIT_SUCCESS; goto finish; + } else if (arg_action == ACTION_BUS_INTROSPECT) { + r = bus_manager_introspect_implementations(stdout, arg_bus_introspect); + retval = r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + goto finish; } assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST)); @@ -2628,6 +2751,13 @@ int main(int argc, char *argv[]) { assert_se(chdir("/") == 0); if (arg_action == ACTION_RUN) { + if (!skip_setup) { + /* Apply the systemd.clock_usec= kernel command line switch */ + apply_clock_update(); + + /* Apply random seed from kernel command line */ + cmdline_take_random_seed(); + } /* A core pattern might have been specified via the cmdline. */ initialize_core_pattern(skip_setup); @@ -2723,8 +2853,8 @@ finish: pager_close(); if (m) { - arg_reboot_watchdog = m->reboot_watchdog; - arg_kexec_watchdog = m->kexec_watchdog; + arg_reboot_watchdog = manager_get_watchdog(m, WATCHDOG_REBOOT); + arg_kexec_watchdog = manager_get_watchdog(m, WATCHDOG_KEXEC); m = manager_free(m); } diff --git a/src/core/manager.c b/src/core/manager.c index 4412e7a84..41e0d7373 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -44,11 +44,13 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "generator-setup.h" #include "hashmap.h" #include "install.h" #include "io-util.h" #include "label.h" #include "locale-setup.h" +#include "load-fragment.h" #include "log.h" #include "macro.h" #include "manager.h" @@ -109,6 +111,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata); static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata); static int manager_run_environment_generators(Manager *m); static int manager_run_generators(Manager *m); +static void manager_vacuum(Manager *m); static usec_t manager_watch_jobs_next_time(Manager *m) { return usec_add(now(CLOCK_MONOTONIC), @@ -180,7 +183,7 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po } } -void manager_flip_auto_status(Manager *m, bool enable, const char *reason) { +static void manager_flip_auto_status(Manager *m, bool enable, const char *reason) { assert(m); if (enable) { @@ -586,6 +589,8 @@ static char** sanitize_environment(char **l) { /* Let's remove some environment variables that we need ourselves to communicate with our clients */ strv_env_unset_many( l, + "CACHE_DIRECTORY", + "CONFIGURATION_DIRECTORY", "EXIT_CODE", "EXIT_STATUS", "INVOCATION_ID", @@ -593,13 +598,16 @@ static char** sanitize_environment(char **l) { "LISTEN_FDNAMES", "LISTEN_FDS", "LISTEN_PID", + "LOGS_DIRECTORY", "MAINPID", "MANAGERPID", "NOTIFY_SOCKET", "PIDFILE", "REMOTE_ADDR", "REMOTE_PORT", + "RUNTIME_DIRECTORY", "SERVICE_RESULT", + "STATE_DIRECTORY", "WATCHDOG_PID", "WATCHDOG_USEC", NULL); @@ -677,19 +685,13 @@ static int manager_setup_prefix(Manager *m) { [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL }, }; - const struct table_entry *p; - ExecDirectoryType i; - int r; - assert(m); - if (MANAGER_IS_SYSTEM(m)) - p = paths_system; - else - p = paths_user; + const struct table_entry *p = MANAGER_IS_SYSTEM(m) ? paths_system : paths_user; + int r; - for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) { - r = sd_path_home(p[i].type, p[i].suffix, &m->prefix[i]); + for (ExecDirectoryType i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) { + r = sd_path_lookup(p[i].type, p[i].suffix, &m->prefix[i]); if (r < 0) return r; } @@ -700,7 +702,7 @@ static int manager_setup_prefix(Manager *m) { static void manager_free_unit_name_maps(Manager *m) { m->unit_id_map = hashmap_free(m->unit_id_map); m->unit_name_map = hashmap_free(m->unit_name_map); - m->unit_path_cache = set_free_free(m->unit_path_cache); + m->unit_path_cache = set_free(m->unit_path_cache); m->unit_cache_mtime = 0; } @@ -778,6 +780,12 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager .original_log_level = -1, .original_log_target = _LOG_TARGET_INVALID, + .watchdog_overridden[WATCHDOG_RUNTIME] = USEC_INFINITY, + .watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY, + .watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY, + + .show_status_overridden = _SHOW_STATUS_INVALID, + .notify_fd = -1, .cgroups_agent_fd = -1, .signal_fd = -1, @@ -833,10 +841,6 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager if (r < 0) return r; - r = hashmap_ensure_allocated(&m->jobs, NULL); - if (r < 0) - return r; - r = hashmap_ensure_allocated(&m->cgroup_unit, &path_hash_ops); if (r < 0) return r; @@ -1338,15 +1342,12 @@ static void manager_clear_jobs_and_units(Manager *m) { } Manager* manager_free(Manager *m) { - ExecDirectoryType dt; - UnitType c; - if (!m) return NULL; manager_clear_jobs_and_units(m); - for (c = 0; c < _UNIT_TYPE_MAX; c++) + for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); @@ -1417,22 +1418,20 @@ Manager* manager_free(Manager *m) { hashmap_free(m->uid_refs); hashmap_free(m->gid_refs); - for (dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) + for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) m->prefix[dt] = mfree(m->prefix[dt]); return mfree(m); } static void manager_enumerate_perpetual(Manager *m) { - UnitType c; - assert(m); if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL) return; /* Let's ask every type to load all units from disk/kernel that it might know */ - for (c = 0; c < _UNIT_TYPE_MAX; c++) { + for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) { if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; @@ -1444,15 +1443,13 @@ static void manager_enumerate_perpetual(Manager *m) { } static void manager_enumerate(Manager *m) { - UnitType c; - assert(m); if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL) return; /* Let's ask every type to load all units from disk/kernel that it might know */ - for (c = 0; c < _UNIT_TYPE_MAX; c++) { + for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) { if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; @@ -1595,20 +1592,6 @@ static void manager_preset_all(Manager *m) { log_info("Populated /etc with preset unit settings."); } -static void manager_vacuum(Manager *m) { - assert(m); - - /* Release any dynamic users no longer referenced */ - dynamic_user_vacuum(m, true); - - /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */ - manager_vacuum_uid_refs(m); - manager_vacuum_gid_refs(m); - - /* Release any runtimes no longer referenced */ - exec_runtime_vacuum(m); -} - static void manager_ready(Manager *m) { assert(m); @@ -1954,6 +1937,21 @@ unsigned manager_dispatch_load_queue(Manager *m) { return n; } +bool manager_unit_file_maybe_loadable_from_cache(Unit *u) { + assert(u); + + if (u->load_state != UNIT_NOT_FOUND) + return false; + + if (u->manager->unit_cache_mtime == 0) + return false; + + if (u->manager->unit_cache_mtime > u->fragment_loadtime) + return true; + + return !lookup_paths_mtime_good(&u->manager->lookup_paths, u->manager->unit_cache_mtime); +} + int manager_load_unit_prepare( Manager *m, const char *name, @@ -1994,18 +1992,31 @@ int manager_load_unit_prepare( ret = manager_get_unit(m, name); if (ret) { - *_ret = ret; - return 1; + /* The time-based cache allows to start new units without daemon-reload, + * but if they are already referenced (because of dependencies or ordering) + * then we have to force a load of the fragment. As an optimization, check + * first if anything in the usual paths was modified since the last time + * the cache was loaded. Also check if the last time an attempt to load the + * unit was made was before the most recent cache refresh, so that we know + * we need to try again - even if the cache is current, it might have been + * updated in a different context before we had a chance to retry loading + * this particular unit. */ + if (manager_unit_file_maybe_loadable_from_cache(ret)) + ret->load_state = UNIT_STUB; + else { + *_ret = ret; + return 1; + } + } else { + ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size); + if (!ret) + return -ENOMEM; } - ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size); - if (!ret) - return -ENOMEM; - if (path) { - ret->fragment_path = strdup(path); - if (!ret->fragment_path) - return -ENOMEM; + r = free_and_strdup(&ret->fragment_path, path); + if (r < 0) + return r; } r = unit_add_name(ret, name); @@ -2287,37 +2298,48 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui return 0; } +static bool manager_process_barrier_fd(char * const *tags, FDSet *fds) { + + /* nothing else must be sent when using BARRIER=1 */ + if (strv_contains(tags, "BARRIER=1")) { + if (strv_length(tags) == 1) { + if (fdset_size(fds) != 1) + log_warning("Got incorrect number of fds with BARRIER=1, closing them."); + } else + log_warning("Extra notification messages sent with BARRIER=1, ignoring everything."); + + /* Drop the message if BARRIER=1 was found */ + return true; + } + + return false; +} + static void manager_invoke_notify_message( Manager *m, Unit *u, const struct ucred *ucred, - const char *buf, + char * const *tags, FDSet *fds) { assert(m); assert(u); assert(ucred); - assert(buf); + assert(tags); if (u->notifygen == m->notifygen) /* Already invoked on this same unit in this same iteration? */ return; u->notifygen = m->notifygen; - if (UNIT_VTABLE(u)->notify_message) { - _cleanup_strv_free_ char **tags = NULL; - - tags = strv_split(buf, NEWLINE); - if (!tags) { - log_oom(); - return; - } - + if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds); - } else if (DEBUG_LOGGING) { - _cleanup_free_ char *x = NULL, *y = NULL; + else if (DEBUG_LOGGING) { + _cleanup_free_ char *buf = NULL, *x = NULL, *y = NULL; - x = ellipsize(buf, 20, 90); + buf = strv_join(tags, ", "); + if (buf) + x = ellipsize(buf, 20, 90); if (x) y = cescape(x); @@ -2334,11 +2356,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t .iov_base = buf, .iov_len = sizeof(buf)-1, }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, @@ -2349,6 +2368,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t struct cmsghdr *cmsg; struct ucred *ucred = NULL; _cleanup_free_ Unit **array_copy = NULL; + _cleanup_strv_free_ char **tags = NULL; Unit *u1, *u2, **array; int r, *fd_array = NULL; size_t n_fds = 0; @@ -2417,8 +2437,17 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } - /* Make sure it's NUL-terminated. */ + /* Make sure it's NUL-terminated, then parse it to obtain the tags list */ buf[n] = 0; + tags = strv_split_newlines(buf); + if (!tags) { + log_oom(); + return 0; + } + + /* possibly a barrier fd, let's see */ + if (manager_process_barrier_fd(tags, fds)) + return 0; /* Increase the generation counter used for filtering out duplicate unit invocations. */ m->notifygen++; @@ -2440,16 +2469,16 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle duplicate units * make sure we only invoke each unit's handler once. */ if (u1) { - manager_invoke_notify_message(m, u1, ucred, buf, fds); + manager_invoke_notify_message(m, u1, ucred, tags, fds); found = true; } if (u2) { - manager_invoke_notify_message(m, u2, ucred, buf, fds); + manager_invoke_notify_message(m, u2, ucred, tags, fds); found = true; } if (array_copy) for (size_t i = 0; array_copy[i]; i++) { - manager_invoke_notify_message(m, array_copy[i], ucred, buf, fds); + manager_invoke_notify_message(m, array_copy[i], ucred, tags, fds); found = true; } @@ -2750,11 +2779,11 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t switch (sfsi.ssi_signo - SIGRTMIN) { case 20: - manager_set_show_status(m, SHOW_STATUS_YES, "signal"); + manager_override_show_status(m, SHOW_STATUS_YES, "signal"); break; case 21: - manager_set_show_status(m, SHOW_STATUS_NO, "signal"); + manager_override_show_status(m, SHOW_STATUS_NO, "signal"); break; case 22: @@ -2903,9 +2932,10 @@ int manager_loop(Manager *m) { return log_error_errno(r, "Failed to enable SIGCHLD event source: %m"); while (m->objective == MANAGER_OK) { - usec_t wait_usec; + usec_t wait_usec, watchdog_usec; - if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) + watchdog_usec = manager_get_watchdog(m, WATCHDOG_RUNTIME); + if (timestamp_is_set(watchdog_usec)) watchdog_ping(); if (!ratelimit_below(&rl)) { @@ -2935,12 +2965,10 @@ int manager_loop(Manager *m) { if (manager_dispatch_dbus_queue(m) > 0) continue; - /* Sleep for half the watchdog time */ - if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) { - wait_usec = m->runtime_watchdog / 2; - if (wait_usec <= 0) - wait_usec = 1; - } else + /* Sleep for watchdog runtime wait time */ + if (timestamp_is_set(watchdog_usec)) + wait_usec = watchdog_runtime_wait(); + else wait_usec = USEC_INFINITY; r = sd_event_run(m->event, wait_usec); @@ -3111,7 +3139,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } int manager_open_serialization(Manager *m, FILE **_f) { - int fd; + _cleanup_close_ int fd = -1; FILE *f; assert(_f); @@ -3120,11 +3148,9 @@ int manager_open_serialization(Manager *m, FILE **_f) { if (fd < 0) return fd; - f = fdopen(fd, "w+"); - if (!f) { - safe_close(fd); + f = take_fdopen(&fd, "w+"); + if (!f) return -errno; - } *_f = f; return 0; @@ -3143,6 +3169,47 @@ static bool manager_timestamp_shall_serialize(ManagerTimestamp t) { MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH); } +#define DESTROY_IPC_FLAG (UINT32_C(1) << 31) + +static void manager_serialize_uid_refs_internal( + Manager *m, + FILE *f, + Hashmap **uid_refs, + const char *field_name) { + + Iterator i; + void *p, *k; + + assert(m); + assert(f); + assert(uid_refs); + assert(field_name); + + /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as + * the actual counter of it is better rebuild after a reload/reexec. */ + + HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) { + uint32_t c; + uid_t uid; + + uid = PTR_TO_UID(k); + c = PTR_TO_UINT32(p); + + if (!(c & DESTROY_IPC_FLAG)) + continue; + + (void) serialize_item_format(f, field_name, UID_FMT, uid); + } +} + +static void manager_serialize_uid_refs(Manager *m, FILE *f) { + manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid"); +} + +static void manager_serialize_gid_refs(Manager *m, FILE *f) { + manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid"); +} + int manager_serialize( Manager *m, FILE *f, @@ -3172,15 +3239,19 @@ int manager_serialize( /* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */ (void) serialize_bool(f, "honor-device-enumeration", !switching_root); - t = show_status_to_string(m->show_status); - if (t) - (void) serialize_item(f, "show-status", t); + if (m->show_status_overridden != _SHOW_STATUS_INVALID) + (void) serialize_item(f, "show-status-overridden", + show_status_to_string(m->show_status_overridden)); if (m->log_level_overridden) (void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level()); if (m->log_target_overridden) (void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target())); + (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]); + for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { _cleanup_free_ char *joined = NULL; @@ -3313,6 +3384,114 @@ static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) { return 0; } +usec_t manager_get_watchdog(Manager *m, WatchdogType t) { + assert(m); + + if (MANAGER_IS_USER(m)) + return USEC_INFINITY; + + if (timestamp_is_set(m->watchdog_overridden[t])) + return m->watchdog_overridden[t]; + + return m->watchdog[t]; +} + +void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) { + int r = 0; + + assert(m); + + if (MANAGER_IS_USER(m)) + return; + + if (m->watchdog[t] == timeout) + return; + + if (t == WATCHDOG_RUNTIME) + if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) { + if (timestamp_is_set(timeout)) + r = watchdog_set_timeout(&timeout); + else + watchdog_close(true); + } + + if (r >= 0) + m->watchdog[t] = timeout; +} + +int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) { + int r = 0; + + assert(m); + + if (MANAGER_IS_USER(m)) + return 0; + + if (m->watchdog_overridden[t] == timeout) + return 0; + + if (t == WATCHDOG_RUNTIME) { + usec_t *p; + + p = timestamp_is_set(timeout) ? &timeout : &m->watchdog[t]; + if (timestamp_is_set(*p)) + r = watchdog_set_timeout(p); + else + watchdog_close(true); + } + + if (r >= 0) + m->watchdog_overridden[t] = timeout; + + return 0; +} + +static void manager_deserialize_uid_refs_one_internal( + Manager *m, + Hashmap** uid_refs, + const char *value) { + + uid_t uid; + uint32_t c; + int r; + + assert(m); + assert(uid_refs); + assert(value); + + r = parse_uid(value, &uid); + if (r < 0 || uid == 0) { + log_debug("Unable to parse UID reference serialization: " UID_FMT, uid); + return; + } + + r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops); + if (r < 0) { + log_oom(); + return; + } + + c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid))); + if (c & DESTROY_IPC_FLAG) + return; + + c |= DESTROY_IPC_FLAG; + + r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)); + if (r < 0) { + log_debug_errno(r, "Failed to add UID reference entry: %m"); + return; + } +} + +static void manager_deserialize_uid_refs_one(Manager *m, const char *value) { + manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value); +} + +static void manager_deserialize_gid_refs_one(Manager *m, const char *value) { + manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value); +} + int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int r = 0; @@ -3409,14 +3588,14 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->honor_device_enumeration = b; - } else if ((val = startswith(l, "show-status="))) { + } else if ((val = startswith(l, "show-status-overridden="))) { ShowStatus s; s = show_status_from_string(val); if (s < 0) - log_notice("Failed to parse show-status flag '%s', ignoring.", val); + log_notice("Failed to parse show-status-overridden flag '%s', ignoring.", val); else - manager_set_show_status(m, s, "deserialization"); + manager_override_show_status(m, s, "deserialize"); } else if ((val = startswith(l, "log-level-override="))) { int level; @@ -3436,6 +3615,30 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else manager_override_log_target(m, target); + } else if ((val = startswith(l, "runtime-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val); + else + manager_override_watchdog(m, WATCHDOG_RUNTIME, t); + + } else if ((val = startswith(l, "reboot-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val); + else + manager_override_watchdog(m, WATCHDOG_REBOOT, t); + + } else if ((val = startswith(l, "kexec-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val); + else + manager_override_watchdog(m, WATCHDOG_KEXEC, t); + } else if (startswith(l, "env=")) { r = deserialize_environment(l + 4, &m->client_environment); if (r < 0) @@ -3487,7 +3690,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else if ((val = startswith(l, "destroy-ipc-gid="))) manager_deserialize_gid_refs_one(m, val); else if ((val = startswith(l, "exec-runtime="))) - exec_runtime_deserialize_one(m, val, fds); + (void) exec_runtime_deserialize_one(m, val, fds); else if ((val = startswith(l, "subscribed="))) { if (strv_extend(&m->deserialized_subscribed, val) < 0) @@ -3792,6 +3995,11 @@ void manager_check_finished(Manager *m) { return; } + /* The jobs hashmap tends to grow a lot during boot, and then it's not reused until shutdown. Let's + kill the hashmap if it is relatively large. */ + if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10) + m->jobs = hashmap_free(m->jobs); + manager_flip_auto_status(m, false, "boot finished"); /* Notify Type=idle units that we are done now */ @@ -3831,25 +4039,9 @@ static bool generator_path_any(const char* const* paths) { return found; } -static const char *const system_env_generator_binary_paths[] = { - "/run/systemd/system-environment-generators", - "/etc/systemd/system-environment-generators", - "/usr/local/lib/systemd/system-environment-generators", - SYSTEM_ENV_GENERATOR_PATH, - NULL -}; - -static const char *const user_env_generator_binary_paths[] = { - "/run/systemd/user-environment-generators", - "/etc/systemd/user-environment-generators", - "/usr/local/lib/systemd/user-environment-generators", - USER_ENV_GENERATOR_PATH, - NULL -}; - static int manager_run_environment_generators(Manager *m) { char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ - const char *const *paths; + _cleanup_strv_free_ char **paths = NULL; void* args[] = { [STDOUT_GENERATE] = &tmp, [STDOUT_COLLECT] = &tmp, @@ -3860,13 +4052,15 @@ static int manager_run_environment_generators(Manager *m) { if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS)) return 0; - paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths; + paths = env_generator_binary_paths(MANAGER_IS_SYSTEM(m)); + if (!paths) + return log_oom(); - if (!generator_path_any(paths)) + if (!generator_path_any((const char* const*) paths)) return 0; RUN_WITH_UMASK(0022) - r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, + r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); return r; } @@ -4090,49 +4284,78 @@ void manager_recheck_journal(Manager *m) { log_open(); } -void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) { +static ShowStatus manager_get_show_status(Manager *m) { assert(m); - assert(mode >= 0 && mode < _SHOW_STATUS_MAX); - if (!MANAGER_IS_SYSTEM(m)) - return; + if (MANAGER_IS_USER(m)) + return _SHOW_STATUS_INVALID; - if (mode == m->show_status) - return; + if (m->show_status_overridden != _SHOW_STATUS_INVALID) + return m->show_status_overridden; - bool enabled = IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES); - log_debug("%s (%s) showing of status (%s).", - enabled ? "Enabling" : "Disabling", - strna(show_status_to_string(mode)), - reason); - m->show_status = mode; + return m->show_status; +} - if (enabled) +bool manager_get_show_status_on(Manager *m) { + assert(m); + + return show_status_on(manager_get_show_status(m)); +} + +static void set_show_status_marker(bool b) { + if (b) (void) touch("/run/systemd/show-status"); else (void) unlink("/run/systemd/show-status"); } -static bool manager_get_show_status(Manager *m, StatusType type) { +void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) { assert(m); + assert(reason); + assert(mode >= 0 && mode < _SHOW_STATUS_MAX); - if (!MANAGER_IS_SYSTEM(m)) - return false; + if (MANAGER_IS_USER(m)) + return; - if (m->no_console_output) - return false; + if (mode == m->show_status) + return; - if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING)) - return false; + if (m->show_status_overridden == _SHOW_STATUS_INVALID) { + bool enabled; - /* If we cannot find out the status properly, just proceed. */ - if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) - return false; + enabled = show_status_on(mode); + log_debug("%s (%s) showing of status (%s).", + enabled ? "Enabling" : "Disabling", + strna(show_status_to_string(mode)), + reason); - if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO) - return true; + set_show_status_marker(enabled); + } - return show_status_on(m->show_status); + m->show_status = mode; +} + +void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason) { + assert(m); + assert(mode < _SHOW_STATUS_MAX); + + if (MANAGER_IS_USER(m)) + return; + + if (mode == m->show_status_overridden) + return; + + m->show_status_overridden = mode; + + if (mode == _SHOW_STATUS_INVALID) + mode = m->show_status; + + log_debug("%s (%s) showing of status (%s).", + m->show_status_overridden != _SHOW_STATUS_INVALID ? "Overriding" : "Restoring", + strna(show_status_to_string(mode)), + reason); + + set_show_status_marker(show_status_on(mode)); } const char *manager_get_confirm_spawn(Manager *m) { @@ -4207,12 +4430,34 @@ bool manager_is_confirm_spawn_disabled(Manager *m) { return access("/run/systemd/confirm_spawn_disabled", F_OK) >= 0; } +static bool manager_should_show_status(Manager *m, StatusType type) { + assert(m); + + if (!MANAGER_IS_SYSTEM(m)) + return false; + + if (m->no_console_output) + return false; + + if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING)) + return false; + + /* If we cannot find out the status properly, just proceed. */ + if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) + return false; + + if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO) + return true; + + return manager_get_show_status_on(m); +} + void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { va_list ap; /* If m is NULL, assume we're after shutdown and let the messages through. */ - if (m && !manager_get_show_status(m, type)) + if (m && !manager_should_show_status(m, type)) return; /* XXX We should totally drop the check for ephemeral here @@ -4247,12 +4492,9 @@ int manager_update_failed_units(Manager *m, Unit *u, bool failed) { size = set_size(m->failed_units); if (failed) { - r = set_ensure_allocated(&m->failed_units, NULL); + r = set_ensure_put(&m->failed_units, NULL, u); if (r < 0) return log_oom(); - - if (set_put(m->failed_units, u) < 0) - return log_oom(); } else (void) set_remove(m->failed_units, u); @@ -4267,6 +4509,11 @@ ManagerState manager_state(Manager *m) { assert(m); + /* Is the special shutdown target active or queued? If so, we are in shutdown state */ + u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); + if (u && unit_active_or_pending(u)) + return MANAGER_STOPPING; + /* Did we ever finish booting? If not then we are still starting up */ if (!MANAGER_IS_FINISHED(m)) { @@ -4277,11 +4524,6 @@ ManagerState manager_state(Manager *m) { return MANAGER_STARTING; } - /* Is the special shutdown target active or queued? If so, we are in shutdown state */ - u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); - if (u && unit_active_or_pending(u)) - return MANAGER_STOPPING; - if (MANAGER_IS_SYSTEM(m)) { /* Are the rescue or emergency targets active or queued? If so we are in maintenance state */ u = manager_get_unit(m, SPECIAL_RESCUE_TARGET); @@ -4300,8 +4542,6 @@ ManagerState manager_state(Manager *m) { return MANAGER_RUNNING; } -#define DESTROY_IPC_FLAG (UINT32_C(1) << 31) - static void manager_unref_uid_internal( Manager *m, Hashmap **uid_refs, @@ -4440,97 +4680,26 @@ static void manager_vacuum_uid_refs_internal( } } -void manager_vacuum_uid_refs(Manager *m) { +static void manager_vacuum_uid_refs(Manager *m) { manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid); } -void manager_vacuum_gid_refs(Manager *m) { +static void manager_vacuum_gid_refs(Manager *m) { manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid); } -static void manager_serialize_uid_refs_internal( - Manager *m, - FILE *f, - Hashmap **uid_refs, - const char *field_name) { - - Iterator i; - void *p, *k; - +static void manager_vacuum(Manager *m) { assert(m); - assert(f); - assert(uid_refs); - assert(field_name); - /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter - * of it is better rebuild after a reload/reexec. */ + /* Release any dynamic users no longer referenced */ + dynamic_user_vacuum(m, true); - HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) { - uint32_t c; - uid_t uid; + /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */ + manager_vacuum_uid_refs(m); + manager_vacuum_gid_refs(m); - uid = PTR_TO_UID(k); - c = PTR_TO_UINT32(p); - - if (!(c & DESTROY_IPC_FLAG)) - continue; - - (void) serialize_item_format(f, field_name, UID_FMT, uid); - } -} - -void manager_serialize_uid_refs(Manager *m, FILE *f) { - manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid"); -} - -void manager_serialize_gid_refs(Manager *m, FILE *f) { - manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid"); -} - -static void manager_deserialize_uid_refs_one_internal( - Manager *m, - Hashmap** uid_refs, - const char *value) { - - uid_t uid; - uint32_t c; - int r; - - assert(m); - assert(uid_refs); - assert(value); - - r = parse_uid(value, &uid); - if (r < 0 || uid == 0) { - log_debug("Unable to parse UID reference serialization: " UID_FMT, uid); - return; - } - - r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops); - if (r < 0) { - log_oom(); - return; - } - - c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid))); - if (c & DESTROY_IPC_FLAG) - return; - - c |= DESTROY_IPC_FLAG; - - r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)); - if (r < 0) { - log_debug_errno(r, "Failed to add UID reference entry: %m"); - return; - } -} - -void manager_deserialize_uid_refs_one(Manager *m, const char *value) { - manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value); -} - -void manager_deserialize_gid_refs_one(Manager *m, const char *value) { - manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value); + /* Release any runtimes no longer referenced */ + exec_runtime_vacuum(m); } int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { diff --git a/src/core/manager.h b/src/core/manager.h index 10c34f954..81b0c13a9 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -114,6 +114,13 @@ typedef enum ManagerTimestamp { _MANAGER_TIMESTAMP_INVALID = -1, } ManagerTimestamp; +typedef enum WatchdogType { + WATCHDOG_RUNTIME, + WATCHDOG_REBOOT, + WATCHDOG_KEXEC, + _WATCHDOG_TYPE_MAX, +} WatchdogType; + #include "execute.h" #include "job.h" #include "path-lookup.h" @@ -231,9 +238,8 @@ struct Manager { char **transient_environment; /* The environment, as determined from config files, kernel cmdline and environment generators */ char **client_environment; /* Environment variables created by clients through the bus API */ - usec_t runtime_watchdog; - usec_t reboot_watchdog; - usec_t kexec_watchdog; + usec_t watchdog[_WATCHDOG_TYPE_MAX]; + usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX]; dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX]; @@ -329,6 +335,7 @@ struct Manager { uint8_t return_value; ShowStatus show_status; + ShowStatus show_status_overridden; StatusUnitFormat status_unit_format; char *confirm_spawn; bool no_console_output; @@ -456,6 +463,7 @@ Unit *manager_get_unit(Manager *m, const char *name); int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); +bool manager_unit_file_maybe_loadable_from_cache(Unit *u); int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret); @@ -506,11 +514,13 @@ void disable_printk_ratelimit(void); void manager_recheck_dbus(Manager *m); void manager_recheck_journal(Manager *m); +bool manager_get_show_status_on(Manager *m); void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason); +void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason); + void manager_set_first_boot(Manager *m, bool b); void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5); -void manager_flip_auto_status(Manager *m, bool enable, const char *reason); Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path); @@ -524,15 +534,6 @@ int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc); void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now); int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now); -void manager_vacuum_uid_refs(Manager *m); -void manager_vacuum_gid_refs(Manager *m); - -void manager_serialize_uid_refs(Manager *m, FILE *f); -void manager_deserialize_uid_refs_one(Manager *m, const char *value); - -void manager_serialize_gid_refs(Manager *m, FILE *f); -void manager_deserialize_gid_refs_one(Manager *m, const char *value); - char *manager_taint_string(Manager *m); void manager_ref_console(Manager *m); @@ -555,5 +556,9 @@ const char *manager_timestamp_to_string(ManagerTimestamp m) _const_; ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_; 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); + 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 3586838f5..fa9510852 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -12,6 +12,8 @@ libcore_shared_sources = ''' '''.split() libcore_sources = ''' + apparmor-setup.c + apparmor-setup.h audit-fd.c audit-fd.h automount.c @@ -72,6 +74,8 @@ libcore_sources = ''' emergency-action.h execute.c execute.h + generator-setup.c + generator-setup.h hostname-setup.c hostname-setup.h ima-setup.c diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 5dfcb6158..feb88f3e6 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -23,6 +23,7 @@ #include "macro.h" #include "mkdir.h" #include "mount-setup.h" +#include "mount-util.h" #include "mountpoint-util.h" #include "nulstr-util.h" #include "path-util.h" @@ -60,51 +61,51 @@ typedef struct MountPoint { #endif static const MountPoint mount_table[] = { - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, + { "devtmpfs", "/dev", "devtmpfs", "mode=755" TMPFS_LIMITS_DEV, MS_NOSUID|MS_NOEXEC|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, #if ENABLE_SMACK - { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV, mac_smack_use, MNT_FATAL }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, mac_smack_use, MNT_FATAL }, #endif - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, + { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, NULL, MNT_IN_CONTAINER }, #if ENABLE_SMACK - { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME, mac_smack_use, MNT_FATAL }, #endif - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup2", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup2", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_legacy_wanted, MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, #if ENABLE_EFI - { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, is_efi_boot, MNT_NONE }, #endif - { "bpf", "/sys/fs/bpf", "bpf", "mode=700", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "bpf", "/sys/fs/bpf", "bpf", "mode=700", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE, }, }; @@ -352,7 +353,7 @@ int mount_cgroup_controllers(void) { } /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */ - (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755"); + (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP); return 0; } @@ -478,7 +479,7 @@ static int relabel_extra(void) { } #endif -int mount_setup(bool loaded_policy) { +int mount_setup(bool loaded_policy, bool leave_propagation) { int r = 0; r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy); @@ -524,7 +525,7 @@ int mount_setup(bool loaded_policy) { * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a * container manager we assume the container manager knows what it is doing (for example, because it set up * some directories with different propagation modes). */ - if (detect_container() <= 0) + if (detect_container() <= 0 && !leave_propagation) if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m"); @@ -534,9 +535,9 @@ int mount_setup(bool loaded_policy) { (void) mkdir_label("/run/systemd", 0755); (void) mkdir_label("/run/systemd/system", 0755); - /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount inaccessible nodes - * from. */ - (void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID); + /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount + * inaccessible nodes from. */ + (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID); return 0; } diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h index b4ca2cf4b..bccd09496 100644 --- a/src/core/mount-setup.h +++ b/src/core/mount-setup.h @@ -4,7 +4,7 @@ #include int mount_setup_early(void); -int mount_setup(bool loaded_policy); +int mount_setup(bool loaded_policy, bool leave_propagation); int mount_cgroup_controllers(void); diff --git a/src/core/mount.c b/src/core/mount.c index 38024d1d2..337e94e90 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -780,6 +780,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { "%sSloppyOptions: %s\n" "%sLazyUnmount: %s\n" "%sForceUnmount: %s\n" + "%sReadWriteOnly: %s\n" "%sTimeoutSec: %s\n", prefix, mount_state_to_string(m->state), prefix, mount_result_to_string(m->result), @@ -795,6 +796,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(m->sloppy_options), prefix, yes_no(m->lazy_unmount), prefix, yes_no(m->force_unmount), + prefix, yes_no(m->read_write_only), prefix, format_timespan(buf, sizeof(buf), m->timeout_usec, USEC_PER_SEC)); if (m->control_pid > 0) @@ -861,6 +863,8 @@ static void mount_enter_dead(Mount *m, MountResult f) { m->result = f; unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result)); + unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_stop); + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); m->exec_runtime = exec_runtime_unref(m->exec_runtime, true); @@ -1006,15 +1010,18 @@ static void mount_enter_mounting(Mount *m) { (void) mkdir_p_label(m->where, m->directory_mode); unit_warn_if_dir_nonempty(UNIT(m), m->where); - unit_warn_leftover_processes(UNIT(m)); + unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_start); m->control_command_id = MOUNT_EXEC_MOUNT; m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; /* Create the source directory for bind-mounts if needed */ p = get_mount_parameters_fragment(m); - if (p && mount_is_bind(p)) - (void) mkdir_p_label(p->what, m->directory_mode); + if (p && mount_is_bind(p)) { + r = mkdir_p_label(p->what, m->directory_mode); + if (r < 0) + log_unit_error_errno(UNIT(m), r, "Failed to make bind mount source '%s': %m", p->what); + } if (p) { _cleanup_free_ char *opts = NULL; @@ -1026,6 +1033,8 @@ static void mount_enter_mounting(Mount *m) { r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); + if (r >= 0 && m->read_write_only) + r = exec_command_append(m->control_command, "-w", NULL); if (r >= 0 && p->fstype) r = exec_command_append(m->control_command, "-t", p->fstype, NULL); if (r >= 0 && !isempty(opts)) @@ -1086,6 +1095,8 @@ static void mount_enter_remounting(Mount *m) { "-o", o, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); + if (r >= 0 && m->read_write_only) + r = exec_command_append(m->control_command, "-w", NULL); if (r >= 0 && p->fstype) r = exec_command_append(m->control_command, "-t", p->fstype, NULL); } else @@ -1666,9 +1677,30 @@ static int mount_setup_unit( if (!is_path(where)) return 0; + /* Mount unit names have to be (like all other unit names) short enough to fit into file names. This + * means there's a good chance that overly long mount point paths after mangling them to look like a + * unit name would result in unit names we don't actually consider valid. This should be OK however + * as such long mount point paths should not happen on regular systems — and if they appear + * nonetheless they are generally synthesized by software, and thus managed by that other + * software. Having such long names just means you cannot use systemd to manage those specific mount + * points, which should be an OK restriction to make. After all we don't have to be able to manage + * all mount points in the world — as long as we don't choke on them when we encounter them. */ r = unit_name_from_path(where, ".mount", &e); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name from path '%s': %m", where); + if (r < 0) { + static RateLimit rate_limit = { /* Let's log about this at warning level at most once every + * 5s. Given that we generate this whenever we read the file + * otherwise we probably shouldn't flood the logs with + * this */ + .interval = 5 * USEC_PER_SEC, + .burst = 1, + }; + + return log_struct_errno( + ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r, + "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR, + "MOUNT_POINT=%s", where, + LOG_MESSAGE("Failed to generate valid unit name from path '%s', ignoring mount point: %m", where)); + } u = manager_get_unit(m, e); if (u) @@ -1678,7 +1710,7 @@ static int mount_setup_unit( * by the sysadmin having called mount(8) directly. */ r = mount_setup_new_unit(m, e, what, where, options, fstype, &flags, &u); if (r < 0) - return log_warning_errno(r, "Failed to set up mount unit: %m"); + return log_warning_errno(r, "Failed to set up mount unit for '%s': %m", where); /* If the mount changed properties or state, let's notify our clients */ if (flags & (MOUNT_PROC_JUST_CHANGED|MOUNT_PROC_JUST_MOUNTED)) @@ -1906,7 +1938,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) { /* Remember that this device might just have disappeared */ if (set_ensure_allocated(&gone, &path_hash_ops) < 0 || - set_put_strdup(gone, mount->parameters_proc_self_mountinfo.what) < 0) + set_put_strdup(&gone, mount->parameters_proc_self_mountinfo.what) < 0) log_oom(); /* we don't care too much about OOM here... */ } @@ -1961,7 +1993,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) { /* Track devices currently used */ if (set_ensure_allocated(&around, &path_hash_ops) < 0 || - set_put_strdup(around, mount->parameters_proc_self_mountinfo.what) < 0) + set_put_strdup(&around, mount->parameters_proc_self_mountinfo.what) < 0) log_oom(); } @@ -2138,7 +2170,6 @@ const UnitVTable mount_vtable = { .control_pid = mount_control_pid, - .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/mount.h b/src/core/mount.h index 07fa05f3c..a1bc2d71a 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -59,6 +59,8 @@ struct Mount { bool lazy_unmount; bool force_unmount; + bool read_write_only; + MountResult result; MountResult reload_result; MountResult clean_result; diff --git a/src/core/namespace.c b/src/core/namespace.c index 90909a07a..36d5ff67a 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -43,6 +43,7 @@ typedef enum MountMode { BIND_MOUNT, BIND_MOUNT_RECURSIVE, PRIVATE_TMP, + PRIVATE_TMP_READONLY, PRIVATE_DEV, BIND_DEV, EMPTY_DIR, @@ -130,9 +131,9 @@ static const MountEntry protect_home_read_only_table[] = { /* ProtectHome=tmpfs table */ static const MountEntry protect_home_tmpfs_table[] = { - { "/home", TMPFS, true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME }, - { "/run/user", TMPFS, true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME }, - { "/root", TMPFS, true, .read_only = true, .options_const = "mode=0700", .flags = MS_NODEV|MS_STRICTATIME }, + { "/home", TMPFS, true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME }, + { "/run/user", TMPFS, true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME }, + { "/root", TMPFS, true, .read_only = true, .options_const = "mode=0700" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME }, }; /* ProtectHome=yes table */ @@ -221,7 +222,7 @@ static const char *mount_entry_path(const MountEntry *p) { static bool mount_entry_read_only(const MountEntry *p) { assert(p); - return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE); + return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE, PRIVATE_TMP_READONLY); } static const char *mount_entry_source(const MountEntry *p) { @@ -295,7 +296,7 @@ static int append_empty_dir_mounts(MountEntry **p, char **strv) { .mode = EMPTY_DIR, .ignore = false, .read_only = true, - .options_const = "mode=755", + .options_const = "mode=755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, }; } @@ -325,23 +326,21 @@ static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n) } static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, size_t n) { - size_t i; - int r; - assert(p); - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { const TemporaryFileSystem *t = tmpfs + i; _cleanup_free_ char *o = NULL, *str = NULL; unsigned long flags; bool ro = false; + int r; if (!path_is_absolute(t->path)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not absolute: %s", t->path); - str = strjoin("mode=0755,", t->options); + str = strjoin("mode=0755" NESTED_TMPFS_LIMITS ",", t->options); if (!str) return -ENOMEM; @@ -649,13 +648,15 @@ add_symlink: return 0; /* Create symlinks like /dev/char/1:9 → ../urandom */ - if (asprintf(&sl, "%s/dev/%s/%u:%u", temporary_mount, S_ISCHR(st.st_mode) ? "char" : "block", major(st.st_rdev), minor(st.st_rdev)) < 0) + if (asprintf(&sl, "%s/dev/%s/%u:%u", + temporary_mount, + S_ISCHR(st.st_mode) ? "char" : "block", + major(st.st_rdev), minor(st.st_rdev)) < 0) return log_oom(); (void) mkdir_parents(sl, 0755); t = strjoina("../", bn); - if (symlink(t, sl) < 0) log_debug_errno(errno, "Failed to symlink '%s' to '%s', ignoring: %m", t, sl); @@ -686,10 +687,15 @@ static int mount_private_dev(MountEntry *m) { dev = strjoina(temporary_mount, "/dev"); (void) mkdir(dev, 0755); - if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) { + if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755" TMPFS_LIMITS_DEV) < 0) { r = log_debug_errno(errno, "Failed to mount tmpfs on '%s': %m", dev); goto fail; } + r = label_fix_container(dev, "/dev", 0); + if (r < 0) { + log_debug_errno(errno, "Failed to fix label of '%s' as /dev: %m", dev); + goto fail; + } devpts = strjoina(temporary_mount, "/dev/pts"); (void) mkdir(devpts, 0755); @@ -742,7 +748,7 @@ static int mount_private_dev(MountEntry *m) { NULSTR_FOREACH(d, devnodes) { r = clone_device_node(d, temporary_mount, &can_mknod); - /* ENXIO means the the *source* is not a device file, skip creation in that case */ + /* ENXIO means the *source* is not a device file, skip creation in that case */ if (r < 0 && r != -ENXIO) goto fail; } @@ -855,15 +861,23 @@ static int mount_procfs(const MountEntry *m) { } static int mount_tmpfs(const MountEntry *m) { + int r; + const char *entry_path = mount_entry_path(m); + const char *source_path = m->path_const; + assert(m); /* First, get rid of everything that is below if there is anything. Then, overmount with our new tmpfs */ - (void) mkdir_p_label(mount_entry_path(m), 0755); - (void) umount_recursive(mount_entry_path(m), 0); + (void) mkdir_p_label(entry_path, 0755); + (void) umount_recursive(entry_path, 0); - if (mount("tmpfs", mount_entry_path(m), "tmpfs", m->flags, mount_entry_options(m)) < 0) - return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m)); + if (mount("tmpfs", entry_path, "tmpfs", m->flags, mount_entry_options(m)) < 0) + return log_debug_errno(errno, "Failed to mount %s: %m", entry_path); + + r = label_fix_container(entry_path, source_path, 0); + if (r < 0) + return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, source_path); return 1; } @@ -930,14 +944,15 @@ static int apply_mount( if (errno == ENOENT && m->ignore) return 0; - return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m)); + return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", + mount_entry_path(m)); } if (geteuid() == 0) - runtime_dir = "/run/systemd"; + runtime_dir = "/run"; else { - if (asprintf(&tmp, "/run/user/"UID_FMT, geteuid()) < 0) - log_oom(); + if (asprintf(&tmp, "/run/user/" UID_FMT, geteuid()) < 0) + return -ENOMEM; runtime_dir = tmp; } @@ -957,8 +972,10 @@ static int apply_mount( if (r == -ENOENT && m->ignore) return 0; if (r < 0) - return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m)); - if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */ + return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", + mount_entry_path(m)); + if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY + * bit for the mount point if needed. */ return 0; /* This isn't a mount point yet, let's make it one. */ what = mount_entry_path(m); @@ -971,9 +988,9 @@ static int apply_mount( case BIND_MOUNT_RECURSIVE: { _cleanup_free_ char *chased = NULL; - /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note that bind - * mount source paths are always relative to the host root, hence we pass NULL as root directory to - * chase_symlinks() here. */ + /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note + * that bind mount source paths are always relative to the host root, hence we pass NULL as + * root directory to chase_symlinks() here. */ r = chase_symlinks(mount_entry_source(m), NULL, CHASE_TRAIL_SLASH, &chased, NULL); if (r == -ENOENT && m->ignore) { @@ -997,6 +1014,7 @@ static int apply_mount( return mount_tmpfs(m); case PRIVATE_TMP: + case PRIVATE_TMP_READONLY: what = mount_entry_source(m); make = true; break; @@ -1026,10 +1044,11 @@ static int apply_mount( if (r == -ENOENT && make) { struct stat st; - /* Hmm, either the source or the destination are missing. Let's see if we can create the destination, then try again */ + /* Hmm, either the source or the destination are missing. Let's see if we can create + the destination, then try again. */ if (stat(what, &st) < 0) - log_debug_errno(errno, "Mount point source '%s' is not accessible: %m", what); + log_error_errno(errno, "Mount point source '%s' is not accessible: %m", what); else { int q; @@ -1041,7 +1060,8 @@ static int apply_mount( q = touch(mount_entry_path(m)); if (q < 0) - log_debug_errno(q, "Failed to create destination mount point node '%s': %m", mount_entry_path(m)); + log_error_errno(q, "Failed to create destination mount point node '%s': %m", + mount_entry_path(m)); else try_again = true; } @@ -1055,14 +1075,14 @@ static int apply_mount( } if (r < 0) - return log_debug_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m)); + return log_error_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m)); } log_debug("Successfully mounted %s to %s", what, mount_entry_path(m)); return 0; } -static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) { +static int make_read_only(const MountEntry *m, char **deny_list, FILE *proc_self_mountinfo) { unsigned long new_flags = 0, flags_mask = 0; bool submounts = false; int r = 0; @@ -1091,7 +1111,7 @@ static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self mount_entry_read_only(m) && !IN_SET(m->mode, EMPTY_DIR, TMPFS); if (submounts) - r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, blacklist, proc_self_mountinfo); + r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, deny_list, proc_self_mountinfo); else r = bind_remount_one_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, proc_self_mountinfo); @@ -1252,15 +1272,23 @@ int setup_namespace( ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags, + const void *root_hash, + size_t root_hash_size, + const char *root_hash_path, + const void *root_hash_sig, + size_t root_hash_sig_size, + const char *root_hash_sig_path, + const char *root_verity, DissectImageFlags dissect_image_flags, char **error_path) { _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; - _cleanup_free_ void *root_hash = NULL; + _cleanup_free_ void *root_hash_decoded = NULL; + _cleanup_free_ char *verity_data = NULL, *hash_sig_path = NULL; MountEntry *m = NULL, *mounts = NULL; - size_t n_mounts, root_hash_size = 0; + size_t n_mounts; bool require_prefix = false; const char *root; int r = 0; @@ -1289,15 +1317,35 @@ int setup_namespace( if (r < 0) return log_debug_errno(r, "Failed to create loop device for root image: %m"); - r = root_hash_load(root_image, &root_hash, &root_hash_size); + r = verity_metadata_load(root_image, + root_hash_path, + root_hash ? NULL : &root_hash_decoded, + root_hash ? NULL : &root_hash_size, + root_verity ? NULL : &verity_data, + root_hash_sig || root_hash_sig_path ? NULL : &hash_sig_path); if (r < 0) return log_debug_errno(r, "Failed to load root hash: %m"); + dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; - r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image); + r = dissect_image(loop_device->fd, + root_hash ?: root_hash_decoded, + root_hash_size, + root_verity ?: verity_data, + dissect_image_flags, + &dissected_image); if (r < 0) return log_debug_errno(r, "Failed to dissect image: %m"); - r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image); + r = dissected_image_decrypt(dissected_image, + NULL, + root_hash ?: root_hash_decoded, + root_hash_size, + root_verity ?: verity_data, + root_hash_sig_path ?: hash_sig_path, + root_hash_sig, + root_hash_sig_size, + dissect_image_flags, + &decrypted_image); if (r < 0) return log_debug_errno(r, "Failed to decrypt dissected image: %m"); } @@ -1358,17 +1406,21 @@ int setup_namespace( goto finish; if (tmp_dir) { + bool ro = streq(tmp_dir, RUN_SYSTEMD_EMPTY); + *(m++) = (MountEntry) { .path_const = "/tmp", - .mode = PRIVATE_TMP, + .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP, .source_const = tmp_dir, }; } if (var_tmp_dir) { + bool ro = streq(var_tmp_dir, RUN_SYSTEMD_EMPTY); + *(m++) = (MountEntry) { .path_const = "/var/tmp", - .mode = PRIVATE_TMP, + .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP, .source_const = var_tmp_dir, }; } @@ -1382,19 +1434,28 @@ int setup_namespace( } if (ns_info->protect_kernel_tunables) { - r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table), ns_info->ignore_protect_paths); + r = append_static_mounts(&m, + protect_kernel_tunables_table, + ELEMENTSOF(protect_kernel_tunables_table), + ns_info->ignore_protect_paths); if (r < 0) goto finish; } if (ns_info->protect_kernel_modules) { - r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table), ns_info->ignore_protect_paths); + r = append_static_mounts(&m, + protect_kernel_modules_table, + ELEMENTSOF(protect_kernel_modules_table), + ns_info->ignore_protect_paths); if (r < 0) goto finish; } if (ns_info->protect_kernel_logs) { - r = append_static_mounts(&m, protect_kernel_logs_table, ELEMENTSOF(protect_kernel_logs_table), ns_info->ignore_protect_paths); + r = append_static_mounts(&m, + protect_kernel_logs_table, + ELEMENTSOF(protect_kernel_logs_table), + ns_info->ignore_protect_paths); if (r < 0) goto finish; } @@ -1415,7 +1476,10 @@ int setup_namespace( goto finish; if (namespace_info_mount_apivfs(ns_info)) { - r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths); + r = append_static_mounts(&m, + apivfs_table, + ELEMENTSOF(apivfs_table), + ns_info->ignore_protect_paths); if (r < 0) goto finish; } @@ -1463,10 +1527,10 @@ int setup_namespace( if (unshare(CLONE_NEWNS) < 0) { r = log_debug_errno(errno, "Failed to unshare the mount namespace: %m"); if (IN_SET(r, -EACCES, -EPERM, -EOPNOTSUPP, -ENOSYS)) - /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter in place - * that doesn't allow us to create namespaces (or a missing cap), then propagate a recognizable - * error back, which the caller can use to detect this case (and only this) and optionally - * continue without namespacing applied. */ + /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter + * in place that doesn't allow us to create namespaces (or a missing cap), then + * propagate a recognizable error back, which the caller can use to detect this case + * (and only this) and optionally continue without namespacing applied. */ r = -ENOANO; goto finish; @@ -1527,11 +1591,11 @@ int setup_namespace( if (n_mounts > 0) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - _cleanup_free_ char **blacklist = NULL; + _cleanup_free_ char **deny_list = NULL; size_t j; - /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc. - * For example, this is the case with the option: 'InaccessiblePaths=/proc' */ + /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of + * /proc. For example, this is the case with the option: 'InaccessiblePaths=/proc'. */ proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); if (!proc_self_mountinfo) { r = log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m"); @@ -1556,10 +1620,10 @@ int setup_namespace( goto finish; } if (r == 0) { - /* We hit a symlinked mount point. The entry got rewritten and might point to a - * very different place now. Let's normalize the changed list, and start from - * the beginning. After all to mount the entry at the new location we might - * need some other mounts first */ + /* We hit a symlinked mount point. The entry got rewritten and might + * point to a very different place now. Let's normalize the changed + * list, and start from the beginning. After all to mount the entry + * at the new location we might need some other mounts first */ again = true; break; } @@ -1580,19 +1644,19 @@ int setup_namespace( normalize_mounts(root, mounts, &n_mounts); } - /* Create a blacklist we can pass to bind_mount_recursive() */ - blacklist = new(char*, n_mounts+1); - if (!blacklist) { + /* Create a deny list we can pass to bind_mount_recursive() */ + deny_list = new(char*, n_mounts+1); + if (!deny_list) { r = -ENOMEM; goto finish; } for (j = 0; j < n_mounts; j++) - blacklist[j] = (char*) mount_entry_path(mounts+j); - blacklist[j] = NULL; + deny_list[j] = (char*) mount_entry_path(mounts+j); + deny_list[j] = NULL; /* Second round, flip the ro bits if necessary. */ for (m = mounts; m < mounts + n_mounts; ++m) { - r = make_read_only(m, blacklist, proc_self_mountinfo); + r = make_read_only(m, deny_list, proc_self_mountinfo); if (r < 0) { if (error_path && mount_entry_path(m)) *error_path = strdup(mount_entry_path(m)); @@ -1763,10 +1827,28 @@ static int make_tmp_prefix(const char *prefix) { } -static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { +static int make_tmp_subdir(const char *parent, char **ret) { + _cleanup_free_ char *y = NULL; + + RUN_WITH_UMASK(0000) { + y = strjoin(parent, "/tmp"); + if (!y) + return -ENOMEM; + + if (mkdir(y, 0777 | S_ISVTX) < 0) + return -errno; + } + + if (ret) + *ret = TAKE_PTR(y); + return 0; +} + +static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) { _cleanup_free_ char *x = NULL; char bid[SD_ID128_STRING_MAX]; sd_id128_t boot_id; + bool rw = true; int r; assert(id); @@ -1789,49 +1871,55 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { return r; RUN_WITH_UMASK(0077) - if (!mkdtemp(x)) - return -errno; + if (!mkdtemp(x)) { + if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno)) + rw = false; + else + return -errno; + } - RUN_WITH_UMASK(0000) { - char *y; + if (rw) { + r = make_tmp_subdir(x, tmp_path); + if (r < 0) + return r; + } else { + /* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being + * read-only. This way the service will get the EROFS result as if it was writing to the real + * file system. */ + r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500); + if (r < 0) + return r; - y = strjoina(x, "/tmp"); - - if (mkdir(y, 0777 | S_ISVTX) < 0) - return -errno; + x = strdup(RUN_SYSTEMD_EMPTY); + if (!x) + return -ENOMEM; } *path = TAKE_PTR(x); - return 0; } int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { - char *a, *b; + _cleanup_(namespace_cleanup_tmpdirp) char *a = NULL; + _cleanup_(rmdir_and_freep) char *a_tmp = NULL; + char *b; int r; assert(id); assert(tmp_dir); assert(var_tmp_dir); - r = setup_one_tmp_dir(id, "/tmp", &a); + r = setup_one_tmp_dir(id, "/tmp", &a, &a_tmp); if (r < 0) return r; - r = setup_one_tmp_dir(id, "/var/tmp", &b); - if (r < 0) { - char *t; - - t = strjoina(a, "/tmp"); - (void) rmdir(t); - (void) rmdir(a); - - free(a); + r = setup_one_tmp_dir(id, "/var/tmp", &b, NULL); + if (r < 0) return r; - } - *tmp_dir = a; - *var_tmp_dir = b; + a_tmp = mfree(a_tmp); /* avoid rmdir */ + *tmp_dir = TAKE_PTR(a); + *var_tmp_dir = TAKE_PTR(b); return 0; } @@ -1964,31 +2052,31 @@ bool ns_type_supported(NamespaceType type) { } static const char *const protect_home_table[_PROTECT_HOME_MAX] = { - [PROTECT_HOME_NO] = "no", - [PROTECT_HOME_YES] = "yes", + [PROTECT_HOME_NO] = "no", + [PROTECT_HOME_YES] = "yes", [PROTECT_HOME_READ_ONLY] = "read-only", - [PROTECT_HOME_TMPFS] = "tmpfs", + [PROTECT_HOME_TMPFS] = "tmpfs", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES); static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { - [PROTECT_SYSTEM_NO] = "no", - [PROTECT_SYSTEM_YES] = "yes", - [PROTECT_SYSTEM_FULL] = "full", + [PROTECT_SYSTEM_NO] = "no", + [PROTECT_SYSTEM_YES] = "yes", + [PROTECT_SYSTEM_FULL] = "full", [PROTECT_SYSTEM_STRICT] = "strict", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES); static const char* const namespace_type_table[] = { - [NAMESPACE_MOUNT] = "mnt", + [NAMESPACE_MOUNT] = "mnt", [NAMESPACE_CGROUP] = "cgroup", - [NAMESPACE_UTS] = "uts", - [NAMESPACE_IPC] = "ipc", - [NAMESPACE_USER] = "user", - [NAMESPACE_PID] = "pid", - [NAMESPACE_NET] = "net", + [NAMESPACE_UTS] = "uts", + [NAMESPACE_IPC] = "ipc", + [NAMESPACE_USER] = "user", + [NAMESPACE_PID] = "pid", + [NAMESPACE_NET] = "net", }; DEFINE_STRING_TABLE_LOOKUP(namespace_type, NamespaceType); diff --git a/src/core/namespace.h b/src/core/namespace.h index ef6c9bdc9..b182223bd 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -12,7 +12,9 @@ typedef struct TemporaryFileSystem TemporaryFileSystem; #include #include "dissect-image.h" +#include "fs-util.h" #include "macro.h" +#include "string-util.h" typedef enum ProtectHome { PROTECT_HOME_NO, @@ -88,9 +90,26 @@ int setup_namespace( ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags, + const void *root_hash, + size_t root_hash_size, + const char *root_hash_path, + const void *root_hash_sig, + size_t root_hash_sig_size, + const char *root_hash_sig_path, + const char *root_verity, DissectImageFlags dissected_image_flags, char **error_path); +#define RUN_SYSTEMD_EMPTY "/run/systemd/empty" + +static inline void namespace_cleanup_tmpdir(char *p) { + PROTECT_ERRNO; + if (!streq_ptr(p, RUN_SYSTEMD_EMPTY)) + (void) rmdir(p); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, namespace_cleanup_tmpdir); + int setup_tmp_dirs( const char *id, char **tmp_dir, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 415b3f5d8..9a5912c10 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -318,6 +318,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="AddDependencyUnitFiles"/> + + type) { case PATH_EXISTS: - b = access(s->path, F_OK) >= 0; - good = b && !s->previous_exists; - s->previous_exists = b; + good = access(s->path, F_OK) >= 0; break; case PATH_EXISTS_GLOB: @@ -200,7 +198,7 @@ static bool path_spec_check_good(PathSpec *s, bool initial) { case PATH_CHANGED: case PATH_MODIFIED: b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; + good = !initial && !from_trigger_notify && b != s->previous_exists; s->previous_exists = b; break; @@ -425,8 +423,7 @@ static void path_set_state(Path *p, PathState state) { old_state = p->state; p->state = state; - if (state != PATH_WAITING && - (state != PATH_RUNNING || p->inotify_triggered)) + if (!IN_SET(state, PATH_WAITING, PATH_RUNNING)) path_unwatch(p); if (state != old_state) @@ -435,7 +432,7 @@ static void path_set_state(Path *p, PathState state) { unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0); } -static void path_enter_waiting(Path *p, bool initial, bool recheck); +static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify); static int path_coldplug(Unit *u) { Path *p = PATH(u); @@ -446,7 +443,7 @@ static int path_coldplug(Unit *u) { if (p->deserialized_state != p->state) { if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING)) - path_enter_waiting(p, true, true); + path_enter_waiting(p, true, false); else path_set_state(p, p->deserialized_state); } @@ -486,8 +483,6 @@ static void path_enter_running(Path *p) { if (r < 0) goto fail; - p->inotify_triggered = false; - path_set_state(p, PATH_RUNNING); path_unwatch(p); @@ -498,27 +493,35 @@ fail: path_enter_dead(p, PATH_FAILURE_RESOURCES); } -static bool path_check_good(Path *p, bool initial) { +static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) { PathSpec *s; assert(p); LIST_FOREACH(spec, s, p->specs) - if (path_spec_check_good(s, initial)) + if (path_spec_check_good(s, initial, from_trigger_notify)) return true; return false; } -static void path_enter_waiting(Path *p, bool initial, bool recheck) { +static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) { + Unit *trigger; int r; - if (recheck) - if (path_check_good(p, initial)) { - log_unit_debug(UNIT(p), "Got triggered."); - path_enter_running(p); - return; - } + /* If the triggered unit is already running, so are we */ + trigger = UNIT_TRIGGER(UNIT(p)); + if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) { + path_set_state(p, PATH_RUNNING); + path_unwatch(p); + return; + } + + if (path_check_good(p, initial, from_trigger_notify)) { + log_unit_debug(UNIT(p), "Got triggered."); + path_enter_running(p); + return; + } r = path_watch(p); if (r < 0) @@ -528,12 +531,11 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { * might have appeared/been removed by now, so we must * recheck */ - if (recheck) - if (path_check_good(p, false)) { - log_unit_debug(UNIT(p), "Got triggered."); - path_enter_running(p); - return; - } + if (path_check_good(p, false, from_trigger_notify)) { + log_unit_debug(UNIT(p), "Got triggered."); + path_enter_running(p); + return; + } path_set_state(p, PATH_WAITING); return; @@ -579,7 +581,7 @@ static int path_start(Unit *u) { path_mkdir(p); p->result = PATH_SUCCESS; - path_enter_waiting(p, true, true); + path_enter_waiting(p, true, false); return 1; } @@ -728,15 +730,10 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v if (changed < 0) goto fail; - /* If we are already running, then remember that one event was - * dispatched so that we restart the service only if something - * actually changed on disk */ - p->inotify_triggered = true; - if (changed) path_enter_running(p); else - path_enter_waiting(p, false, true); + path_enter_waiting(p, false, false); return 0; @@ -760,11 +757,11 @@ static void path_trigger_notify(Unit *u, Unit *other) { if (p->state == PATH_RUNNING && UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { log_unit_debug(UNIT(p), "Got notified about unit deactivation."); - - /* Hmm, so inotify was triggered since the - * last activation, so I guess we need to - * recheck what is going on. */ - path_enter_waiting(p, false, p->inotify_triggered); + path_enter_waiting(p, false, true); + } else if (p->state == PATH_WAITING && + !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_unit_debug(UNIT(p), "Got notified about unit activation."); + path_enter_waiting(p, false, true); } } @@ -831,6 +828,5 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_vtable = bus_path_vtable, .bus_set_property = bus_path_set_property, }; diff --git a/src/core/path.h b/src/core/path.h index 4d4b6236c..9e2836535 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -56,8 +56,6 @@ struct Path { PathState state, deserialized_state; - bool inotify_triggered; - bool make_directory; mode_t directory_mode; diff --git a/src/core/scope.c b/src/core/scope.c index 76358c416..42c51b086 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -635,6 +635,9 @@ const UnitVTable scope_vtable = { .kill = scope_kill, + .freeze = unit_freeze_vtable_common, + .thaw = unit_thaw_vtable_common, + .get_timeout = scope_get_timeout, .serialize = scope_serialize, @@ -649,7 +652,6 @@ const UnitVTable scope_vtable = { .notify_cgroup_empty = scope_notify_cgroup_empty_event, - .bus_vtable = bus_scope_vtable, .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 00f5241cd..1d52b5ff0 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -123,12 +123,12 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { fmt2 = strjoina("selinux: ", fmt); va_start(ap, fmt); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" + + DISABLE_WARNING_FORMAT_NONLITERAL; log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, PROJECT_FILE, __LINE__, __FUNCTION__, fmt2, ap); -#pragma GCC diagnostic pop + REENABLE_WARNING; va_end(ap); return 0; @@ -143,16 +143,17 @@ static int access_init(sd_bus_error *error) { return 1; if (avc_open(NULL, 0) != 0) { - int enforce, saved_errno = errno; + int saved_errno = errno; + bool enforce; - enforce = security_getenforce(); - log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m"); + enforce = security_getenforce() != 0; + log_full_errno(enforce ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m"); /* If enforcement isn't on, then let's suppress this * error, and just don't do any AVC checks. The * warning we printed is hence all the admin will * see. */ - if (enforce == 0) + if (!enforce) return 0; /* Return an access denied error, if we couldn't load @@ -185,7 +186,7 @@ int mac_selinux_generic_access_check( _cleanup_free_ char *cl = NULL; _cleanup_freecon_ char *fcon = NULL; char **cmdline = NULL; - bool enforce = false; /* Will be set to the real value later if needed */ + bool enforce; int r = 0; assert(message); @@ -196,6 +197,9 @@ int mac_selinux_generic_access_check( if (r <= 0) return r; + /* delay call until we checked in `access_init()` if SELinux is actually enabled */ + enforce = security_getenforce() != 0; + r = sd_bus_query_sender_creds( message, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID| @@ -223,9 +227,8 @@ int mac_selinux_generic_access_check( if (getfilecon_raw(path, &fcon) < 0) { r = -errno; - enforce = security_getenforce() > 0; - log_warning_errno(r, "SELinux getfilecon_raw on '%s' failed%s (perm=%s): %m", + log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m", path, enforce ? "" : ", ignoring", permission); @@ -240,9 +243,8 @@ int mac_selinux_generic_access_check( } else { if (getcon_raw(&fcon) < 0) { r = -errno; - enforce = security_getenforce() > 0; - log_warning_errno(r, "SELinux getcon_raw failed%s (perm=%s): %m", + log_warning_errno(r, "SELinux getcon_raw() failed%s (perm=%s): %m", enforce ? "" : ", ignoring", permission); if (!enforce) @@ -266,14 +268,13 @@ int mac_selinux_generic_access_check( r = selinux_check_access(scon, fcon, tclass, permission, &audit_info); if (r < 0) { r = errno_or_else(EPERM); - enforce = security_getenforce() > 0; if (enforce) sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); } - log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %m", - scon, fcon, tclass, permission, path, cl); + log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m", + scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", path, cl); return enforce ? r : 0; } diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index da2e6cbd7..58f737de0 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -7,17 +7,8 @@ int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error); -#if HAVE_SELINUX - #define mac_selinux_access_check(message, permission, error) \ mac_selinux_generic_access_check((message), NULL, (permission), (error)) #define mac_selinux_unit_access_check(unit, message, permission, error) \ mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error)) - -#else - -#define mac_selinux_access_check(message, permission, error) 0 -#define mac_selinux_unit_access_check(unit, message, permission, error) 0 - -#endif diff --git a/src/core/service.c b/src/core/service.c index db8cf370d..00e61945b 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -56,6 +56,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_FAILED] = UNIT_FAILED, @@ -79,6 +80,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, [SERVICE_FAILED] = UNIT_FAILED, @@ -198,15 +200,6 @@ static void service_stop_watchdog(Service *s) { s->watchdog_timestamp = DUAL_TIMESTAMP_NULL; } -static usec_t service_get_watchdog_usec(Service *s) { - assert(s); - - if (s->watchdog_override_enable) - return s->watchdog_override_usec; - - return s->watchdog_original_usec; -} - static void service_start_watchdog(Service *s) { usec_t watchdog_usec; int r; @@ -423,7 +416,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us return 0; } -static int service_add_fd_store(Service *s, int fd, const char *name) { +static int service_add_fd_store(Service *s, int fd, const char *name, bool do_poll) { ServiceFDStore *fs; int r; @@ -447,25 +440,31 @@ static int service_add_fd_store(Service *s, int fd, const char *name) { } } - fs = new0(ServiceFDStore, 1); + fs = new(ServiceFDStore, 1); if (!fs) return -ENOMEM; - fs->fd = fd; - fs->service = s; - fs->fdname = strdup(name ?: "stored"); + *fs = (ServiceFDStore) { + .fd = fd, + .service = s, + .do_poll = do_poll, + .fdname = strdup(name ?: "stored"), + }; + if (!fs->fdname) { free(fs); return -ENOMEM; } - r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs); - if (r < 0 && r != -EPERM) { /* EPERM indicates fds that aren't pollable, which is OK */ - free(fs->fdname); - free(fs); - return r; - } else if (r >= 0) - (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); + if (do_poll) { + r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs); + if (r < 0 && r != -EPERM) { /* EPERM indicates fds that aren't pollable, which is OK */ + free(fs->fdname); + free(fs); + return r; + } else if (r >= 0) + (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); + } LIST_PREPEND(fd_store, s->fd_store, fs); s->n_fd_store++; @@ -473,7 +472,7 @@ static int service_add_fd_store(Service *s, int fd, const char *name) { return 1; /* fd newly stored */ } -static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { +static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bool do_poll) { int r; assert(s); @@ -485,7 +484,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { if (fd < 0) break; - r = service_add_fd_store(s, fd, name); + r = service_add_fd_store(s, fd, name, do_poll); if (r == -EXFULL) return log_unit_warning_errno(UNIT(s), r, "Cannot store more fds than FileDescriptorStoreMax=%u, closing remaining.", @@ -655,7 +654,7 @@ static void service_fix_stdio(Service *s) { /* Note that EXEC_INPUT_NULL and EXEC_OUTPUT_INHERIT play a special role here: they are both the * default value that is subject to automatic overriding triggered by other settings and an explicit - * choice the user can make. We don't distuingish between these cases currently. */ + * choice the user can make. We don't distinguish between these cases currently. */ if (s->exec_context.std_input == EXEC_INPUT_NULL && s->exec_context.stdin_data_size > 0) @@ -860,10 +859,14 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sRestartSec: %s\n" "%sTimeoutStartSec: %s\n" - "%sTimeoutStopSec: %s\n", + "%sTimeoutStopSec: %s\n" + "%sTimeoutStartFailureMode: %s\n" + "%sTimeoutStopFailureMode: %s\n", prefix, format_timespan(buf_restart, sizeof(buf_restart), s->restart_usec, USEC_PER_SEC), prefix, format_timespan(buf_start, sizeof(buf_start), s->timeout_start_usec, USEC_PER_SEC), - prefix, format_timespan(buf_stop, sizeof(buf_stop), s->timeout_stop_usec, USEC_PER_SEC)); + prefix, format_timespan(buf_stop, sizeof(buf_stop), s->timeout_stop_usec, USEC_PER_SEC), + prefix, service_timeout_failure_mode_to_string(s->timeout_start_failure_mode), + prefix, service_timeout_failure_mode_to_string(s->timeout_stop_failure_mode)); if (s->timeout_abort_set) fprintf(f, @@ -1075,7 +1078,7 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -1084,7 +1087,7 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { service_unwatch_main_pid(s); s->main_command = NULL; } @@ -1093,7 +1096,7 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) { service_unwatch_control_pid(s); s->control_command = NULL; @@ -1109,7 +1112,7 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && !(state == SERVICE_DEAD && UNIT(s)->job)) service_close_socket_fd(s); @@ -1157,6 +1160,7 @@ static usec_t service_coldplug_timeout(Service *s) { return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec); case SERVICE_STOP_WATCHDOG: + case SERVICE_FINAL_WATCHDOG: return usec_add(UNIT(s)->state_change_timestamp.monotonic, service_timeout_abort_usec(s)); case SERVICE_AUTO_RESTART: @@ -1190,7 +1194,7 @@ static int service_coldplug(Unit *u) { SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) { r = unit_watch_pid(UNIT(s), s->main_pid, false); if (r < 0) return r; @@ -1202,7 +1206,7 @@ static int service_coldplug(Unit *u) { SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) { r = unit_watch_pid(UNIT(s), s->control_pid, false); if (r < 0) @@ -1438,17 +1442,15 @@ static int service_spawn( pid_t *_pid) { _cleanup_(exec_params_clear) ExecParameters exec_params = { - .flags = flags, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, - .exec_fd = -1, + .flags = flags, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, + .exec_fd = -1, }; - _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL; - size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0; - _cleanup_close_ int exec_fd = -1; - _cleanup_free_ int *fds = NULL; + _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL; + size_t n_env = 0; pid_t pid; int r; @@ -1473,17 +1475,21 @@ static int service_spawn( s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds, &n_storage_fds); + r = service_collect_fds(s, + &exec_params.fds, + &exec_params.fd_names, + &exec_params.n_socket_fds, + &exec_params.n_storage_fds); if (r < 0) return r; - log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds); + log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds); } if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) { assert(!s->exec_fd_event_source); - r = service_allocate_exec_fd(s, &exec_fd_source, &exec_fd); + r = service_allocate_exec_fd(s, &exec_fd_source, &exec_params.exec_fd); if (r < 0) return r; } @@ -1523,7 +1529,6 @@ static int service_spawn( if (getpeername(s->socket_fd, &sa.sa, &salen) >= 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) { - _cleanup_free_ char *addr = NULL; char *t; unsigned port; @@ -1578,10 +1583,6 @@ static int service_spawn( MANAGER_IS_SYSTEM(UNIT(s)->manager) && unit_has_name(UNIT(s), SPECIAL_DBUS_SERVICE)); strv_free_and_replace(exec_params.environment, final_env); - exec_params.fds = fds; - exec_params.fd_names = fd_names; - exec_params.n_socket_fds = n_socket_fds; - exec_params.n_storage_fds = n_storage_fds; exec_params.watchdog_usec = service_get_watchdog_usec(s); exec_params.selinux_context_net = s->socket_fd_selinux_context_net; if (s->type == SERVICE_IDLE) @@ -1589,7 +1590,6 @@ static int service_spawn( exec_params.stdin_fd = s->stdin_fd; exec_params.stdout_fd = s->stdout_fd; exec_params.stderr_fd = s->stderr_fd; - exec_params.exec_fd = exec_fd; r = exec_spawn(UNIT(s), c, @@ -1752,6 +1752,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) unit_log_failure(UNIT(s), service_result_to_string(s->result)); end_state = SERVICE_FAILED; } + unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop); if (!allow_restart) log_unit_debug(UNIT(s), "Service restart not allowed."); @@ -1861,6 +1862,7 @@ static int state_to_kill_operation(Service *s, ServiceState state) { switch (state) { case SERVICE_STOP_WATCHDOG: + case SERVICE_FINAL_WATCHDOG: return KILL_WATCHDOG; case SERVICE_STOP_SIGTERM: @@ -1881,7 +1883,7 @@ static int state_to_kill_operation(Service *s, ServiceState state) { } static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { - int r; + int kill_operation, r; assert(s); @@ -1895,10 +1897,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f * died now */ (void) unit_enqueue_rewatch_pids(UNIT(s)); + kill_operation = state_to_kill_operation(s, state); r = unit_kill_context( UNIT(s), &s->kill_context, - state_to_kill_operation(s, state), + kill_operation, s->main_pid, s->control_pid, s->main_pid_alien); @@ -1907,7 +1910,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f if (r > 0) { r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), - state == SERVICE_STOP_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec)); + kill_operation == KILL_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec)); if (r < 0) goto fail; @@ -1916,7 +1919,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS); else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) service_enter_stop_post(s, SERVICE_SUCCESS); - else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill) + else if (IN_SET(state, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM) && s->kill_context.send_sigkill) service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); else service_enter_dead(s, SERVICE_SUCCESS, true); @@ -2078,15 +2081,16 @@ static int service_adverse_to_leftover_processes(Service *s) { assert(s); /* KillMode=mixed and control group are used to indicate that all process should be killed off. - * SendSIGKILL is used for services that require a clean shutdown. These are typically database - * service where a SigKilled process would result in a lengthy recovery and who's shutdown or - * startup time is quite variable (so Timeout settings aren't of use). + * SendSIGKILL= is used for services that require a clean shutdown. These are typically database + * service where a SigKilled process would result in a lengthy recovery and who's shutdown or startup + * time is quite variable (so Timeout settings aren't of use). * * Here we take these two factors and refuse to start a service if there are existing processes * within a control group. Databases, while generally having some protection against multiple - * instances running, lets not stress the rigor of these. Also ExecStartPre parts of the service + * instances running, lets not stress the rigor of these. Also ExecStartPre= parts of the service * aren't as rigoriously written to protect aganst against multiple use. */ - if (unit_warn_leftover_processes(UNIT(s)) && + + if (unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start) > 0 && IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) && !s->kill_context.send_sigkill) return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY), @@ -2124,9 +2128,9 @@ static void service_enter_start(Service *s) { if (!c) { if (s->type != SERVICE_ONESHOT) { - /* There's no command line configured for the main command? Hmm, that is strange. This can only - * happen if the configuration changes at runtime. In this case, let's enter a failure - * state. */ + /* There's no command line configured for the main command? Hmm, that is strange. + * This can only happen if the configuration changes at runtime. In this case, + * let's enter a failure state. */ log_unit_error(UNIT(s), "There's no 'start' task anymore we could start."); r = -ENXIO; goto fail; @@ -2445,7 +2449,7 @@ static int service_start(Unit *u) { * please! */ if (IN_SET(s->state, SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING)) return -EAGAIN; /* Already on it! */ @@ -2516,7 +2520,7 @@ static int service_stop(Unit *u) { /* Already on it */ if (IN_SET(s->state, SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) return 0; /* A restart will be scheduled or is in progress. */ @@ -2569,6 +2573,8 @@ static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecC ExecCommand *first, *c; assert(s); + assert(id >= 0); + assert(id < _SERVICE_EXEC_COMMAND_MAX); first = s->exec_command[id]; @@ -2632,10 +2638,12 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command p = cescape(command->path); if (!p) - return -ENOMEM; + return log_oom(); key = strjoina(type, "-command"); - return serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args); + (void) serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args); + + return 0; } static int service_serialize(Unit *u, FILE *f, FDSet *fds) { @@ -2711,7 +2719,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (!c) return log_oom(); - (void) serialize_item_format(f, "fd-store-fd", "%i %s", copy, c); + (void) serialize_item_format(f, "fd-store-fd", "%i \"%s\" %i", copy, c, fs->do_poll); } if (s->main_exec_status.pid > 0) { @@ -2737,7 +2745,11 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { return 0; } -static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) { +static int service_deserialize_exec_command( + Unit *u, + const char *key, + const char *value) { + Service *s = SERVICE(u); int r; unsigned idx = 0, i; @@ -2826,9 +2838,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char break; } - if (command && control) + if (command && control) { s->control_command = command; - else if (command) + s->control_command_id = id; + } else if (command) s->main_command = command; else log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed."); @@ -2935,30 +2948,36 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->socket_fd = fdset_remove(fds, fd); } } else if (streq(key, "fd-store-fd")) { - const char *fdv; - size_t pf; + _cleanup_free_ char *fdv = NULL, *fdn = NULL, *fdp = NULL; int fd; + int do_poll; - pf = strcspn(value, WHITESPACE); - fdv = strndupa(value, pf); - - if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + r = extract_first_word(&value, &fdv, NULL, 0); + if (r <= 0 || safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) { log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value); - else { - _cleanup_free_ char *t = NULL; - const char *fdn; - - fdn = value + pf; - fdn += strspn(fdn, WHITESPACE); - (void) cunescape(fdn, 0, &t); - - r = service_add_fd_store(s, fd, t); - if (r < 0) - log_unit_error_errno(u, r, "Failed to add fd to store: %m"); - else - fdset_remove(fds, fd); + return 0; } + r = extract_first_word(&value, &fdn, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE); + if (r <= 0) { + log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\": %m", value); + return 0; + } + + r = extract_first_word(&value, &fdp, NULL, 0); + if (r == 0) { + /* If the value is not present, we assume the default */ + do_poll = 1; + } else if (r < 0 || safe_atoi(fdp, &do_poll) < 0) { + log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\": %m", value); + return 0; + } + + r = service_add_fd_store(s, fd, fdn, do_poll); + if (r < 0) + log_unit_error_errno(u, r, "Failed to add fd to store: %m"); + else + fdset_remove(fds, fd); } else if (streq(key, "main-exec-status-pid")) { pid_t pid; @@ -3307,6 +3326,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { break; case SERVICE_STOP_POST: + case SERVICE_FINAL_WATCHDOG: case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: if (main_pid_good(s) <= 0 && control_pid_good(s) <= 0) @@ -3507,6 +3527,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; + case SERVICE_FINAL_WATCHDOG: case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: @@ -3660,6 +3681,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); break; + case SERVICE_FINAL_WATCHDOG: case SERVICE_FINAL_SIGTERM: case SERVICE_FINAL_SIGKILL: if (main_pid_good(s) <= 0) @@ -3706,13 +3728,32 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_CONDITION: case SERVICE_START_PRE: case SERVICE_START: - log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state)); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - case SERVICE_START_POST: - log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + switch (s->timeout_start_failure_mode) { + + case SERVICE_TIMEOUT_TERMINATE: + log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state)); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_ABORT: + log_unit_warning(UNIT(s), "%s operation timed out. Aborting.", service_state_to_string(s->state)); + service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_KILL: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "%s operation timed out. Killing.", service_state_to_string(s->state)); + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "%s operation timed out. Skipping SIGKILL.", service_state_to_string(s->state)); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); + } + break; + + default: + assert_not_reached("unknown timeout mode"); + } break; case SERVICE_RUNNING: @@ -3728,17 +3769,48 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us break; case SERVICE_STOP: - log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + switch (s->timeout_stop_failure_mode) { + + case SERVICE_TIMEOUT_TERMINATE: + log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_ABORT: + log_unit_warning(UNIT(s), "Stopping timed out. Aborting."); + service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_KILL: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Stopping timed out. Killing."); + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL."); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); + } + break; + + default: + assert_not_reached("unknown timeout mode"); + } break; case SERVICE_STOP_WATCHDOG: - log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Terminating."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Killing."); + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Skipping SIGKILL."); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); + } break; case SERVICE_STOP_SIGTERM: - if (s->kill_context.send_sigkill) { + if (s->timeout_stop_failure_mode == SERVICE_TIMEOUT_ABORT) { + log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Aborting."); + service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT); + } else if (s->kill_context.send_sigkill) { log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Killing."); service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { @@ -3758,16 +3830,52 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us break; case SERVICE_STOP_POST: - log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating."); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); + switch (s->timeout_stop_failure_mode) { + + case SERVICE_TIMEOUT_TERMINATE: + log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating."); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_ABORT: + log_unit_warning(UNIT(s), "State 'stop-post' timed out. Aborting."); + service_enter_signal(s, SERVICE_FINAL_WATCHDOG, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_TIMEOUT_KILL: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'stop-post' timed out. Killing."); + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "State 'stop-post' timed out. Skipping SIGKILL. Entering failed mode."); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); + } + break; + + default: + assert_not_reached("unknown timeout mode"); + } + break; + + case SERVICE_FINAL_WATCHDOG: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'final-watchdog' timed out. Killing."); + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "State 'final-watchdog' timed out. Skipping SIGKILL. Entering failed mode."); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); + } break; case SERVICE_FINAL_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Killing."); + if (s->timeout_stop_failure_mode == SERVICE_TIMEOUT_ABORT) { + log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Aborting."); + service_enter_signal(s, SERVICE_FINAL_WATCHDOG, SERVICE_FAILURE_TIMEOUT); + } else if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Killing."); service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Skipping SIGKILL. Entering failed mode."); + log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Skipping SIGKILL. Entering failed mode."); service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); } @@ -3829,7 +3937,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void return 0; } -static bool service_notify_message_authorized(Service *s, pid_t pid, char **tags, FDSet *fds) { +static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) { assert(s); if (s->notify_access == NOTIFY_NONE) { @@ -3876,19 +3984,19 @@ static void service_force_watchdog(Service *s) { static void service_notify_message( Unit *u, const struct ucred *ucred, - char **tags, + char * const *tags, FDSet *fds) { Service *s = SERVICE(u); bool notify_dbus = false; const char *e; - char **i; + char * const *i; int r; assert(u); assert(ucred); - if (!service_notify_message_authorized(SERVICE(u), ucred->pid, tags, fds)) + if (!service_notify_message_authorized(SERVICE(u), ucred->pid, fds)) return; if (DEBUG_LOGGING) { @@ -4059,7 +4167,7 @@ static void service_notify_message( name = NULL; } - (void) service_add_fd_store_set(s, fds, name); + (void) service_add_fd_store_set(s, fds, name, !strv_contains(tags, "FDPOLL=0")); } /* Notify clients about changed status or main pid */ @@ -4249,6 +4357,7 @@ static bool service_needs_console(Unit *u) { SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL); } @@ -4403,6 +4512,14 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); +static const char* const service_timeout_failure_mode_table[_SERVICE_TIMEOUT_FAILURE_MODE_MAX] = { + [SERVICE_TIMEOUT_TERMINATE] = "terminate", + [SERVICE_TIMEOUT_ABORT] = "abort", + [SERVICE_TIMEOUT_KILL] = "kill", +}; + +DEFINE_STRING_TABLE_LOOKUP(service_timeout_failure_mode, ServiceTimeoutFailureMode); + const UnitVTable service_vtable = { .object_size = sizeof(Service), .exec_context_offset = offsetof(Service, exec_context), @@ -4440,6 +4557,9 @@ const UnitVTable service_vtable = { .clean = service_clean, .can_clean = service_can_clean, + .freeze = unit_freeze_vtable_common, + .thaw = unit_thaw_vtable_common, + .serialize = service_serialize, .deserialize_item = service_deserialize_item, @@ -4463,7 +4583,6 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, - .bus_vtable = bus_service_vtable, .bus_set_property = bus_service_set_property, .bus_commit_properties = bus_service_commit_properties, diff --git a/src/core/service.h b/src/core/service.h index 11e861a3d..4423f893b 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -74,12 +74,21 @@ typedef enum ServiceResult { _SERVICE_RESULT_INVALID = -1 } ServiceResult; +typedef enum ServiceTimeoutFailureMode { + SERVICE_TIMEOUT_TERMINATE, + SERVICE_TIMEOUT_ABORT, + SERVICE_TIMEOUT_KILL, + _SERVICE_TIMEOUT_FAILURE_MODE_MAX, + _SERVICE_TIMEOUT_FAILURE_MODE_INVALID = -1 +} ServiceTimeoutFailureMode; + struct ServiceFDStore { Service *service; int fd; char *fdname; sd_event_source *event_source; + bool do_poll; LIST_FIELDS(ServiceFDStore, fd_store); }; @@ -102,6 +111,8 @@ struct Service { usec_t timeout_abort_usec; bool timeout_abort_set; usec_t runtime_max_usec; + ServiceTimeoutFailureMode timeout_start_failure_mode; + ServiceTimeoutFailureMode timeout_stop_failure_mode; dual_timestamp watchdog_timestamp; usec_t watchdog_usec; /* the requested watchdog timeout in the unit file */ @@ -199,6 +210,11 @@ static inline usec_t service_timeout_abort_usec(Service *s) { return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec; } +static inline usec_t service_get_watchdog_usec(Service *s) { + assert(s); + return s->watchdog_override_enable ? s->watchdog_override_usec : s->watchdog_original_usec; +} + extern const UnitVTable service_vtable; int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net); @@ -222,6 +238,9 @@ NotifyState notify_state_from_string(const char *s) _pure_; const char* service_result_to_string(ServiceResult i) _const_; ServiceResult service_result_from_string(const char *s) _pure_; +const char* service_timeout_failure_mode_to_string(ServiceTimeoutFailureMode i) _const_; +ServiceTimeoutFailureMode service_timeout_failure_mode_from_string(const char *s) _pure_; + DEFINE_CAST(SERVICE, Service); #define STATUS_TEXT_MAX (16U*1024U) diff --git a/src/core/slice.c b/src/core/slice.c index d97a26278..f4f63fcb5 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "dbus-slice.h" #include "dbus-unit.h" +#include "fd-util.h" #include "log.h" #include "serialize.h" #include "slice.h" @@ -347,6 +348,82 @@ static void slice_enumerate_perpetual(Manager *m) { (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL); } +static bool slice_freezer_action_supported_by_children(Unit *s) { + Unit *member; + void *v; + Iterator i; + + assert(s); + + HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) { + int r; + + if (UNIT_DEREF(member->slice) != s) + continue; + + if (member->type == UNIT_SLICE) { + r = slice_freezer_action_supported_by_children(member); + if (!r) + return r; + } + + if (!UNIT_VTABLE(member)->freeze) + return false; + } + + return true; +} + +static int slice_freezer_action(Unit *s, FreezerAction action) { + Unit *member; + void *v; + Iterator i; + int r; + + assert(s); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + + if (!slice_freezer_action_supported_by_children(s)) + return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice"); + + HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) { + if (UNIT_DEREF(member->slice) != s) + continue; + + if (action == FREEZER_FREEZE) + r = UNIT_VTABLE(member)->freeze(member); + else + r = UNIT_VTABLE(member)->thaw(member); + + if (r < 0) + return r; + } + + r = unit_cgroup_freezer_action(s, action); + if (r < 0) + return r; + + return 1; +} + +static int slice_freeze(Unit *s) { + assert(s); + + return slice_freezer_action(s, FREEZER_FREEZE); +} + +static int slice_thaw(Unit *s) { + assert(s); + + return slice_freezer_action(s, FREEZER_THAW); +} + +static bool slice_can_freeze(Unit *s) { + assert(s); + + return slice_freezer_action_supported_by_children(s); +} + const UnitVTable slice_vtable = { .object_size = sizeof(Slice), .cgroup_context_offset = offsetof(Slice, cgroup_context), @@ -371,13 +448,16 @@ const UnitVTable slice_vtable = { .kill = slice_kill, + .freeze = slice_freeze, + .thaw = slice_thaw, + .can_freeze = slice_can_freeze, + .serialize = slice_serialize, .deserialize_item = slice_deserialize_item, .active_state = slice_active_state, .sub_state_to_string = slice_sub_state_to_string, - .bus_vtable = bus_slice_vtable, .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, diff --git a/src/core/socket.c b/src/core/socket.c index 4a0e3a253..127195c9f 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -205,38 +205,25 @@ static int socket_arm_timer(Socket *s, usec_t usec) { return 0; } -int socket_instantiate_service(Socket *s) { - _cleanup_free_ char *prefix = NULL, *name = NULL; +static int socket_instantiate_service(Socket *s, int cfd) { + Unit *service; int r; - Unit *u; assert(s); + assert(cfd >= 0); - /* This fills in s->service if it isn't filled in yet. For - * Accept=yes sockets we create the next connection service - * here. For Accept=no this is mostly a NOP since the service - * is figured out at load time anyway. */ + /* This fills in s->service if it isn't filled in yet. For Accept=yes sockets we create the next + * connection service here. For Accept=no this is mostly a NOP since the service is figured out at + * load time anyway. */ - if (UNIT_DEREF(s->service)) - return 0; - - if (!s->accept) - return 0; - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); + r = socket_load_service_unit(s, cfd, &service); if (r < 0) return r; - if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) - return -ENOMEM; + unit_ref_set(&s->service, UNIT(s), service); - r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); - if (r < 0) - return r; - - unit_ref_set(&s->service, UNIT(s), u); - - return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false, UNIT_DEPENDENCY_IMPLICIT); + return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service, + false, UNIT_DEPENDENCY_IMPLICIT); } static bool have_non_accept_socket(Socket *s) { @@ -635,6 +622,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sBroadcast: %s\n" "%sPassCredentials: %s\n" "%sPassSecurity: %s\n" + "%sPassPacketInfo: %s\n" "%sTCPCongestion: %s\n" "%sRemoveOnStop: %s\n" "%sWritable: %s\n" @@ -654,6 +642,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->broadcast), prefix, yes_no(s->pass_cred), prefix, yes_no(s->pass_sec), + prefix, yes_no(s->pass_pktinfo), prefix, strna(s->tcp_congestion), prefix, yes_no(s->remove_on_stop), prefix, yes_no(s->writable), @@ -1070,6 +1059,12 @@ static void socket_apply_socket_options(Socket *s, int fd) { log_unit_warning_errno(UNIT(s), r, "SO_PASSSEC failed: %m"); } + if (s->pass_pktinfo) { + r = socket_pass_pktinfo(fd, true); + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m"); + } + if (s->priority >= 0) { r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, s->priority); if (r < 0) @@ -1398,37 +1393,81 @@ clear: return r; } +int socket_load_service_unit(Socket *s, int cfd, Unit **ret) { + /* Figure out what the unit that will be used to handle the connections on the socket looks like. + * + * If cfd < 0, then we don't have a connection yet. In case of Accept=yes sockets, use a fake + * instance name. + */ + + if (UNIT_ISSET(s->service)) { + *ret = UNIT_DEREF(s->service); + return 0; + } + + if (!s->accept) + return -ENODATA; + + /* Build the instance name and load the unit */ + _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; + int r; + + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) + return r; + + if (cfd >= 0) { + r = instance_from_socket(cfd, s->n_accepted, &instance); + if (r == -ENOTCONN) + /* ENOTCONN is legitimate if TCP RST was received. + * This connection is over, but the socket unit lives on. */ + return log_unit_debug_errno(UNIT(s), r, + "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); + if (r < 0) + return r; + } + + /* For accepting sockets, we don't know how the instance will be called until we get a connection and + * can figure out what the peer name is. So let's use "internal" as the instance to make it clear + * that this is not an actual peer name. We use "unknown" when we cannot figure out the peer. */ + r = unit_name_build(prefix, instance ?: "internal", ".service", &name); + if (r < 0) + return r; + + return manager_load_unit(UNIT(s)->manager, name, NULL, NULL, ret); +} + static int socket_determine_selinux_label(Socket *s, char **ret) { - Service *service; - ExecCommand *c; - _cleanup_free_ char *path = NULL; int r; assert(s); assert(ret); if (s->selinux_context_from_net) { - /* If this is requested, get label from the network label */ + /* If this is requested, get the label from the network label */ r = mac_selinux_get_our_label(ret); if (r == -EOPNOTSUPP) goto no_label; } else { - /* Otherwise, get it from the executable we are about to start */ - r = socket_instantiate_service(s); + /* Otherwise, get it from the executable we are about to start. */ + + Unit *service; + ExecCommand *c; + _cleanup_free_ char *path = NULL; + + r = socket_load_service_unit(s, -1, &service); + if (r == -ENODATA) + goto no_label; if (r < 0) return r; - if (!UNIT_ISSET(s->service)) - goto no_label; - - service = SERVICE(UNIT_DEREF(s->service)); - c = service->exec_command[SERVICE_EXEC_START]; + c = SERVICE(service)->exec_command[SERVICE_EXEC_START]; if (!c) goto no_label; - r = chase_symlinks(c->path, service->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL); + r = chase_symlinks(c->path, SERVICE(service)->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL); if (r < 0) goto no_label; @@ -1614,8 +1653,8 @@ static int socket_open_fds(Socket *_s) { case SOCKET_SOCKET: if (!know_label) { - /* Figure out label, if we don't it know yet. We do it once, for the first socket where - * we need this and remember it for the rest. */ + /* Figure out the label, if we don't it know yet. We do it once for the first + * socket where we need this and remember it for the rest. */ r = socket_determine_selinux_label(s, &label); if (r < 0) @@ -2035,6 +2074,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) { else unit_log_failure(UNIT(s), socket_result_to_string(s->result)); + unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop); + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); s->exec_runtime = exec_runtime_unref(s->exec_runtime, true); @@ -2237,7 +2278,7 @@ static void socket_enter_start_pre(Socket *s) { socket_unwatch_control_pid(s); - unit_warn_leftover_processes(UNIT(s)); + unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start); s->control_command_id = SOCKET_EXEC_START_PRE; s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; @@ -2330,7 +2371,6 @@ static void socket_enter_running(Socket *s, int cfd) { socket_set_state(s, SOCKET_RUNNING); } else { - _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL; Service *service; @@ -2342,9 +2382,9 @@ static void socket_enter_running(Socket *s, int cfd) { if (s->max_connections_per_source > 0) { r = socket_acquire_peer(s, cfd, &p); - if (r < 0) { + if (r < 0) goto refuse; - } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + if (r > 0 && p->n_ref > s->max_connections_per_source) { _cleanup_free_ char *t = NULL; (void) sockaddr_pretty(&p->peer.sa, p->peer_salen, true, false, &t); @@ -2356,30 +2396,7 @@ static void socket_enter_running(Socket *s, int cfd) { } } - r = socket_instantiate_service(s); - if (r < 0) - goto fail; - - r = instance_from_socket(cfd, s->n_accepted, &instance); - if (r < 0) { - if (r != -ENOTCONN) - goto fail; - - /* ENOTCONN is legitimate if TCP RST was received. - * This connection is over, but the socket unit lives on. */ - log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); - goto refuse; - } - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); - if (r < 0) - goto fail; - - r = unit_name_build(prefix, instance, ".service", &name); - if (r < 0) - goto fail; - - r = unit_add_name(UNIT_DEREF(s->service), name); + r = socket_instantiate_service(s, cfd); if (r < 0) goto fail; @@ -2387,21 +2404,20 @@ static void socket_enter_running(Socket *s, int cfd) { unit_ref_unset(&s->service); s->n_accepted++; - unit_choose_id(UNIT(service), name); r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); if (r < 0) goto fail; - cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ + TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) { - /* We failed to activate the new service, but it still exists. Let's make sure the service - * closes and forgets the connection fd again, immediately. */ + /* We failed to activate the new service, but it still exists. Let's make sure the + * service closes and forgets the connection fd again, immediately. */ service_close_socket_fd(service); goto fail; } @@ -3462,7 +3478,6 @@ const UnitVTable socket_vtable = { .control_pid = socket_control_pid, - .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/socket.h b/src/core/socket.h index 9e0be15ba..bb14e6b0f 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -121,6 +121,7 @@ struct Socket { bool broadcast; bool pass_cred; bool pass_sec; + bool pass_pktinfo; /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ SocketAddressBindIPv6Only bind_ipv6_only; @@ -165,7 +166,7 @@ void socket_connection_unref(Socket *s); void socket_free_ports(Socket *s); -int socket_instantiate_service(Socket *s); +int socket_load_service_unit(Socket *s, int cfd, Unit **ret); char *socket_fdname(Socket *s); diff --git a/src/core/swap.c b/src/core/swap.c index c5945371d..20179de2d 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -701,6 +701,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { s->result = f; unit_log_result(UNIT(s), s->result == SWAP_SUCCESS, swap_result_to_string(s->result)); + unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop); swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); s->exec_runtime = exec_runtime_unref(s->exec_runtime, true); @@ -788,7 +789,7 @@ static void swap_enter_activating(Swap *s) { assert(s); - unit_warn_leftover_processes(UNIT(s)); + unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start); s->control_command_id = SWAP_EXEC_ACTIVATE; s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE; @@ -1655,7 +1656,6 @@ const UnitVTable swap_vtable = { .control_pid = swap_control_pid, - .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/system.conf.in b/src/core/system.conf.in index 811212546..40bb54888 100644 --- a/src/core/system.conf.in +++ b/src/core/system.conf.in @@ -16,6 +16,7 @@ #LogTarget=journal-or-kmsg #LogColor=yes #LogLocation=no +#LogTime=no #DumpCore=yes #ShowStatus=yes #CrashChangeVT=no diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in index 8331832c7..842483782 100644 --- a/src/core/systemd.pc.in +++ b/src/core/systemd.pc.in @@ -7,36 +7,93 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. +# Names with prefixes are preferred, and the run-together names should be +# considered deprecated (though there is no plan to remove them). New names +# shall have underscores. + prefix=@prefix@ -rootprefix=@rootprefix_noslash@ -sysconfdir=@sysconfdir@ -systemdutildir=${rootprefix}/lib/systemd -systemdsystemunitdir=${rootprefix}/lib/systemd/system -systemdsystempresetdir=${rootprefix}/lib/systemd/system-preset -systemduserunitdir=${prefix}/lib/systemd/user -systemduserpresetdir=${prefix}/lib/systemd/user-preset -systemdsystemconfdir=${sysconfdir}/systemd/system -systemduserconfdir=${sysconfdir}/systemd/user -systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system -systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user -systemdsystemgeneratordir=${rootprefix}/lib/systemd/system-generators -systemdusergeneratordir=${prefix}/lib/systemd/user-generators -systemdsystemgeneratorpath=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemdsystemgeneratordir} -systemdusergeneratorpath=/run/systemd/user-generators:/etc/systemd/user-generators:/usr/local/lib/systemd/user-generators:${systemdusergeneratordir} -systemdsleepdir=${rootprefix}/lib/systemd/system-sleep -systemdshutdowndir=${rootprefix}/lib/systemd/system-shutdown -tmpfilesdir=${prefix}/lib/tmpfiles.d -sysusersdir=${prefix}/lib/sysusers.d -sysctldir=${prefix}/lib/sysctl.d -binfmtdir=${prefix}/lib/binfmt.d -modulesloaddir=${prefix}/lib/modules-load.d -catalogdir=${prefix}/lib/systemd/catalog -systemuidmax=@systemuidmax@ -systemgidmax=@systemgidmax@ -dynamicuidmin=@dynamicuidmin@ -dynamicuidmax=@dynamicuidmax@ -containeruidbasemin=@containeruidbasemin@ -containeruidbasemax=@containeruidbasemax@ +root_prefix=@rootprefix_noslash@ +rootprefix=${root_prefix} +sysconf_dir=@sysconfdir@ +sysconfdir=${sysconf_dir} + +systemd_util_dir=${root_prefix}/lib/systemd +systemdutildir=${systemd_util_dir} + +systemd_system_unit_dir=${rootprefix}/lib/systemd/system +systemdsystemunitdir=${systemd_system_unit_dir} + +systemd_system_preset_dir=${rootprefix}/lib/systemd/system-preset +systemdsystempresetdir=${systemd_system_preset_dir} + +systemd_user_unit_dir=${prefix}/lib/systemd/user +systemduserunitdir=${systemd_user_unit_dir} + +systemd_user_preset_dir=${prefix}/lib/systemd/user-preset +systemduserpresetdir=${systemd_user_preset_dir} + +systemd_system_conf_dir=${sysconfdir}/systemd/system +systemdsystemconfdir=${systemd_system_conf_dir} + +systemd_user_conf_dir=${sysconfdir}/systemd/user +systemduserconfdir=${systemd_user_conf_dir} + +systemd_system_unit_path=${systemd_system_conf_dir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemd_system_unit_dir}:/usr/lib/systemd/system:/lib/systemd/system +systemdsystemunitpath=${systemd_system_unit_path} + +systemd_user_unit_path=${systemd_user_conf_dir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemd_user_unit_dir}:/usr/lib/systemd/user:/usr/share/systemd/user +systemduserunitpath=${systemd_user_unit_path} + +systemd_system_generator_dir=${root_prefix}/lib/systemd/system-generators +systemdsystemgeneratordir=${systemd_system_generator_dir} + +systemd_user_generator_dir=${prefix}/lib/systemd/user-generators +systemdusergeneratordir=${systemd_user_generator_dir} + +systemd_system_generator_path=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemd_system_generator_dir} +systemdsystemgeneratorpath=${systemd_system_generator_path} + +systemd_user_generator_path=/run/systemd/user-generators:/etc/systemd/user-generators:/usr/local/lib/systemd/user-generators:${systemd_user_generator_dir} +systemdusergeneratorpath=${systemd_user_generator_path} + +systemd_sleep_dir=${root_prefix}/lib/systemd/system-sleep +systemdsleepdir=${systemd_sleep_dir} + +systemd_shutdown_dir=${root_prefix}/lib/systemd/system-shutdown +systemdshutdowndir=${systemd_shutdown_dir} + +tmpfiles_dir=${prefix}/lib/tmpfiles.d +tmpfilesdir=${tmpfiles_dir} + +sysusers_dir=${rootprefix}/lib/sysusers.d +sysusersdir=${sysusers_dir} + +sysctl_dir=${rootprefix}/lib/sysctl.d +sysctldir=${sysctl_dir} + +binfmt_dir=${rootprefix}/lib/binfmt.d +binfmtdir=${binfmt_dir} + +modules_load_dir=${rootprefix}/lib/modules-load.d +modulesloaddir=${modules_load_dir} + +catalog_dir=${prefix}/lib/systemd/catalog +catalogdir=${catalog_dir} + +system_uid_max=@systemuidmax@ +systemuidmax=${system_uid_max} +system_gid_max=@systemgidmax@ +systemgidmax=${system_gid_max} + +dynamic_uid_min=@dynamicuidmin@ +dynamicuidmin=${dynamic_uid_min} +dynamic_uid_max=@dynamicuidmax@ +dynamicuidmax=${dynamic_uid_max} + +container_uid_base_min=@containeruidbasemin@ +containeruidbasemin=${container_uid_base_min} +container_uid_base_max=@containeruidbasemax@ +containeruidbasemax=${container_uid_base_max} Name: systemd Description: systemd System and Service Manager diff --git a/src/core/target.c b/src/core/target.c index e433e46ef..65affcec1 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -209,8 +209,6 @@ const UnitVTable target_vtable = { .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, - .bus_vtable = bus_target_vtable, - .status_message_formats = { .finished_start_job = { [JOB_DONE] = "Reached target %s.", diff --git a/src/core/timer.c b/src/core/timer.c index 57d979d52..03a9c14f7 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -367,7 +367,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) { continue; if (v->base == TIMER_CALENDAR) { - usec_t b; + usec_t b, rebased; /* If we know the last time this was * triggered, schedule the job based relative @@ -387,12 +387,14 @@ static void timer_enter_waiting(Timer *t, bool time_change) { if (r < 0) continue; - /* To make the delay due to RandomizedDelaySec= work even at boot, - * if the scheduled time has already passed, set the time when systemd - * first started as the scheduled time. - * Also, we don't have to check t->persistent since the logic implicitly express true. */ - if (v->next_elapse < UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime) - v->next_elapse = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime; + /* To make the delay due to RandomizedDelaySec= work even at boot, if the scheduled + * time has already passed, set the time when systemd first started as the scheduled + * time. Note that we base this on the monotonic timestamp of the boot, not the + * realtime one, since the wallclock might have been off during boot. */ + rebased = map_clock_usec(UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic, + CLOCK_MONOTONIC, CLOCK_REALTIME); + if (v->next_elapse < rebased) + v->next_elapse = rebased; if (!found_realtime) t->next_elapse_realtime = v->next_elapse; @@ -925,6 +927,5 @@ const UnitVTable timer_vtable = { .time_change = timer_time_change, .timezone_change = timer_timezone_change, - .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, }; diff --git a/src/core/transaction.c b/src/core/transaction.c index 6dc4e95be..4a57b8e3f 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -336,9 +336,8 @@ static char* merge_unit_ids(const char* unit_log_field, char **pairs) { STRV_FOREACH_PAIR(unit_id, job_type, pairs) { next = strlen(unit_log_field) + strlen(*unit_id); - if (!GREEDY_REALLOC(ans, alloc, size + next + 1)) { + if (!GREEDY_REALLOC(ans, alloc, size + next + 1)) return mfree(ans); - } sprintf(ans + size, "%s%s", unit_log_field, *unit_id); if (*(unit_id+1)) @@ -657,6 +656,10 @@ static int transaction_apply( assert(!j->transaction_prev); assert(!j->transaction_next); + r = hashmap_ensure_allocated(&m->jobs, NULL); + if (r < 0) + return r; + r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j); if (r < 0) goto rollback; @@ -951,6 +954,24 @@ int transaction_add_job_and_dependencies( if (type != JOB_STOP) { r = bus_unit_validate_load_state(unit, e); + /* The time-based cache allows to start new units without daemon-reload, + * but if they are already referenced (because of dependencies or ordering) + * then we have to force a load of the fragment. As an optimization, check + * first if anything in the usual paths was modified since the last time + * the cache was loaded. Also check if the last time an attempt to load the + * unit was made was before the most recent cache refresh, so that we know + * we need to try again - even if the cache is current, it might have been + * updated in a different context before we had a chance to retry loading + * this particular unit. + * Given building up the transaction is a synchronous operation, attempt + * to load the unit immediately. */ + if (r < 0 && manager_unit_file_maybe_loadable_from_cache(unit)) { + unit->load_state = UNIT_STUB; + r = unit_load(unit); + if (r < 0 || unit->load_state == UNIT_STUB) + unit->load_state = UNIT_NOT_FOUND; + r = bus_unit_validate_load_state(unit, e); + } if (r < 0) return r; } diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 2daaaf997..4fee5dc6d 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -186,8 +186,14 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) { * %u: the username of the running user * * %m: the machine ID of the running system - * %H: the host name of the running system * %b: the boot ID of the running system + * %H: the hostname of the running system + * %v: the kernel version + * %a: the native userspace architecture + * %o: the OS ID according to /etc/os-release + * %w: the OS version ID, according to /etc/os-release + * %B: the OS build ID, according to /etc/os-release + * %W: the OS variant ID, according to /etc/os-release */ const Specifier table[] = { @@ -203,8 +209,14 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) { { 'u', specifier_user_name, NULL }, { 'm', specifier_machine_id, NULL }, - { 'H', specifier_host_name, NULL }, { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, {} }; @@ -279,6 +291,7 @@ int unit_full_printf(const Unit *u, const char *format, char **ret) { { 'm', specifier_machine_id, NULL }, { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, { 'b', specifier_boot_id, NULL }, { 'v', specifier_kernel_release, NULL }, {} diff --git a/src/core/unit.c b/src/core/unit.c index 92c37fc59..2c09def06 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -93,10 +93,6 @@ Unit *unit_new(Manager *m, size_t size) { if (!u) return NULL; - u->names = set_new(&string_hash_ops); - if (!u->names) - return mfree(u); - u->manager = m; u->type = _UNIT_TYPE_INVALID; u->default_dependencies = true; @@ -152,7 +148,8 @@ bool unit_has_name(const Unit *u, const char *name) { assert(u); assert(name); - return set_contains(u->names, (char*) name); + return streq_ptr(name, u->id) || + set_contains(u->aliases, name); } static void unit_init(Unit *u) { @@ -187,8 +184,16 @@ static void unit_init(Unit *u) { if (ec) { exec_context_init(ec); - ec->keyring_mode = MANAGER_IS_SYSTEM(u->manager) ? - EXEC_KEYRING_SHARED : EXEC_KEYRING_INHERIT; + if (MANAGER_IS_SYSTEM(u->manager)) + ec->keyring_mode = EXEC_KEYRING_SHARED; + else { + ec->keyring_mode = EXEC_KEYRING_INHERIT; + + /* User manager might have its umask redefined by PAM or UMask=. In this + * case let the units it manages inherit this value by default. They can + * still tune this value through their own unit file */ + (void) get_process_umask(getpid_cached(), &ec->umask); + } } kc = unit_get_kill_context(u); @@ -199,8 +204,25 @@ static void unit_init(Unit *u) { UNIT_VTABLE(u)->init(u); } +static int unit_add_alias(Unit *u, char *donated_name) { + int r; + + /* Make sure that u->names is allocated. We may leave u->names + * empty if we fail later, but this is not a problem. */ + r = set_ensure_allocated(&u->aliases, &string_hash_ops); + if (r < 0) + return r; + + r = set_put(u->aliases, donated_name); + if (r < 0) + return r; + assert(r > 0); + + return 0; +} + int unit_add_name(Unit *u, const char *text) { - _cleanup_free_ char *s = NULL, *i = NULL; + _cleanup_free_ char *name = NULL, *instance = NULL; UnitType t; int r; @@ -208,90 +230,101 @@ int unit_add_name(Unit *u, const char *text) { assert(text); if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) { - if (!u->instance) - return -EINVAL; + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), + "instance is not set when adding name '%s': %m", text); - r = unit_name_replace_instance(text, u->instance, &s); + r = unit_name_replace_instance(text, u->instance, &name); if (r < 0) - return r; + return log_unit_debug_errno(u, r, + "failed to build instance name from '%s': %m", text); } else { - s = strdup(text); - if (!s) + name = strdup(text); + if (!name) return -ENOMEM; } - if (set_contains(u->names, s)) + if (unit_has_name(u, name)) return 0; - if (hashmap_contains(u->manager->units, s)) - return -EEXIST; - if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return -EINVAL; + if (hashmap_contains(u->manager->units, name)) + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST), + "unit already exist when adding name '%s': %m", name); - t = unit_name_to_type(s); + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), + "name '%s' is invalid: %m", name); + + t = unit_name_to_type(name); if (t < 0) - return -EINVAL; + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), + "failed to derive unit type from name '%s': %m", name); if (u->type != _UNIT_TYPE_INVALID && t != u->type) - return -EINVAL; + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), + "unit type is illegal: u->type(%d) and t(%d) for name '%s': %m", + u->type, t, name); - r = unit_name_to_instance(s, &i); + r = unit_name_to_instance(name, &instance); if (r < 0) - return r; + return log_unit_debug_errno(u, r, "failed to extract instance from name '%s': %m", name); - if (i && !unit_type_may_template(t)) - return -EINVAL; + if (instance && !unit_type_may_template(t)) + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), "templates are not allowed for name '%s': %m", name); - /* Ensure that this unit is either instanced or not instanced, - * but not both. Note that we do allow names with different - * instance names however! */ - if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) - return -EINVAL; + /* Ensure that this unit either has no instance, or that the instance matches. */ + if (u->type != _UNIT_TYPE_INVALID && !streq_ptr(u->instance, instance)) + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), + "cannot add name %s, the instances don't match (\"%s\" != \"%s\").", + name, instance, u->instance); - if (!unit_type_may_alias(t) && !set_isempty(u->names)) - return -EEXIST; + if (u->id && !unit_type_may_alias(t)) + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST), + "cannot add name %s, aliases are not allowed for %s units.", + name, unit_type_to_string(t)); if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) - return -E2BIG; + return log_unit_warning_errno(u, SYNTHETIC_ERRNO(E2BIG), "cannot add name, manager has too many units: %m"); - r = set_put(u->names, s); + /* Add name to the global hashmap first, because that's easier to undo */ + r = hashmap_put(u->manager->units, name, u); if (r < 0) - return r; - assert(r > 0); + return log_unit_debug_errno(u, r, "add unit to hashmap failed for name '%s': %m", text); - r = hashmap_put(u->manager->units, s, u); - if (r < 0) { - (void) set_remove(u->names, s); - return r; - } + if (u->id) { + r = unit_add_alias(u, name); /* unit_add_alias() takes ownership of the name on success */ + if (r < 0) { + hashmap_remove(u->manager->units, name); + return r; + } + TAKE_PTR(name); + + } else { + /* A new name, we don't need the set yet. */ + assert(u->type == _UNIT_TYPE_INVALID); + assert(!u->instance); - if (u->type == _UNIT_TYPE_INVALID) { u->type = t; - u->id = s; - u->instance = TAKE_PTR(i); + u->id = TAKE_PTR(name); + u->instance = TAKE_PTR(instance); LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); - unit_init(u); } - s = NULL; - unit_add_to_dbus_queue(u); return 0; } int unit_choose_id(Unit *u, const char *name) { _cleanup_free_ char *t = NULL; - char *s, *i; + char *s; int r; assert(u); assert(name); if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - if (!u->instance) return -EINVAL; @@ -302,21 +335,22 @@ int unit_choose_id(Unit *u, const char *name) { name = t; } - /* Selects one of the names of this unit as the id */ - s = set_get(u->names, (char*) name); + if (streq_ptr(u->id, name)) + return 0; /* Nothing to do. */ + + /* Selects one of the aliases of this unit as the id */ + s = set_get(u->aliases, (char*) name); if (!s) return -ENOENT; - /* Determine the new instance from the new id */ - r = unit_name_to_instance(s, &i); - if (r < 0) - return r; - - u->id = s; - - free(u->instance); - u->instance = i; + if (u->id) { + r = set_remove_and_put(u->aliases, name, u->id); + if (r < 0) + return r; + } else + assert_se(set_remove(u->aliases, name)); /* see set_get() above… */ + u->id = s; /* Old u->id is now stored in the set, and s is not stored anywhere */ unit_add_to_dbus_queue(u); return 0; @@ -484,9 +518,7 @@ static void bidi_set_free(Unit *u, Hashmap *h) { /* Frees the hashmap and makes sure we are dropped from the inverse pointers */ HASHMAP_FOREACH_KEY(v, other, h, i) { - UnitDependency d; - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) hashmap_remove(other->dependencies[d], u); unit_add_to_gc_queue(other); @@ -582,7 +614,6 @@ static void unit_done(Unit *u) { } void unit_free(Unit *u) { - UnitDependency d; Iterator i; char *t; @@ -611,11 +642,14 @@ void unit_free(Unit *u) { sd_bus_slot_unref(u->match_bus_slot); sd_bus_track_unref(u->bus_track); u->deserialized_refs = strv_free(u->deserialized_refs); + u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); unit_free_requires_mounts_for(u); - SET_FOREACH(t, u->names, i) + SET_FOREACH(t, u->aliases, i) hashmap_remove_value(u->manager->units, t, u); + if (u->id) + hashmap_remove_value(u->manager->units, u->id, u); if (!sd_id128_is_null(u->invocation_id)) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u); @@ -632,7 +666,7 @@ void unit_free(Unit *u) { job_free(j); } - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) bidi_set_free(u, u->dependencies[d]); if (u->on_console) @@ -712,14 +746,46 @@ void unit_free(Unit *u) { free(u->instance); free(u->job_timeout_reboot_arg); - - set_free_free(u->names); - free(u->reboot_arg); + set_free_free(u->aliases); + free(u->id); + free(u); } +FreezerState unit_freezer_state(Unit *u) { + assert(u); + + return u->freezer_state; +} + +int unit_freezer_state_kernel(Unit *u, FreezerState *ret) { + char *values[1] = {}; + int r; + + assert(u); + + r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", + STRV_MAKE("frozen"), values); + if (r < 0) + return r; + + r = _FREEZER_STATE_INVALID; + + if (values[0]) { + if (streq(values[0], "0")) + r = FREEZER_RUNNING; + else if (streq(values[0], "1")) + r = FREEZER_FROZEN; + } + + free(values[0]); + *ret = r; + + return 0; +} + UnitActiveState unit_active_state(Unit *u) { assert(u); @@ -739,21 +805,6 @@ const char* unit_sub_state_to_string(Unit *u) { return UNIT_VTABLE(u)->sub_state_to_string(u); } -static int set_complete_move(Set **s, Set **other) { - assert(s); - assert(other); - - if (!other) - return 0; - - if (*s) - return set_move(*s, *other); - else - *s = TAKE_PTR(*other); - - return 0; -} - static int hashmap_complete_move(Hashmap **s, Hashmap **other) { assert(s); assert(other); @@ -770,23 +821,28 @@ static int hashmap_complete_move(Hashmap **s, Hashmap **other) { } static int merge_names(Unit *u, Unit *other) { - char *t; + char *name; Iterator i; int r; assert(u); assert(other); - r = set_complete_move(&u->names, &other->names); + r = unit_add_alias(u, other->id); if (r < 0) return r; - set_free_free(other->names); - other->names = NULL; - other->id = NULL; + r = set_move(u->aliases, other->aliases); + if (r < 0) { + set_remove(u->aliases, other->id); + return r; + } - SET_FOREACH(t, u->names, i) - assert_se(hashmap_replace(u->manager->units, t, u) == 0); + TAKE_PTR(other->id); + other->aliases = set_free_free(other->aliases); + + SET_FOREACH(name, u->aliases, i) + assert_se(hashmap_replace(u->manager->units, name, u) == 0); return 0; } @@ -825,19 +881,17 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD assert(d < _UNIT_DEPENDENCY_MAX); /* Fix backwards pointers. Let's iterate through all dependent units of the other unit. */ - HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i) { - UnitDependency k; + HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i) - /* Let's now iterate through the dependencies of that dependencies of the other units, looking for - * pointers back, and let's fix them up, to instead point to 'u'. */ - - for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) { + /* Let's now iterate through the dependencies of that dependencies of the other units, + * looking for pointers back, and let's fix them up, to instead point to 'u'. */ + for (UnitDependency k = 0; k < _UNIT_DEPENDENCY_MAX; k++) if (back == u) { /* Do not add dependencies between u and itself. */ if (hashmap_remove(back->dependencies[k], other)) maybe_warn_about_dependency(u, other_id, k); } else { - UnitDependencyInfo di_u, di_other, di_merged; + UnitDependencyInfo di_u, di_other; /* Let's drop this dependency between "back" and "other", and let's create it between * "back" and "u" instead. Let's merge the bit masks of the dependency we are moving, @@ -849,7 +903,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD di_u.data = hashmap_get(back->dependencies[k], u); - di_merged = (UnitDependencyInfo) { + UnitDependencyInfo di_merged = { .origin_mask = di_u.origin_mask | di_other.origin_mask, .destination_mask = di_u.destination_mask | di_other.destination_mask, }; @@ -861,9 +915,6 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD /* assert_se(hashmap_remove_and_replace(back->dependencies[k], other, u, di_merged.data) >= 0); */ } - } - - } /* Also do not move dependencies on u to itself */ back = hashmap_remove(other->dependencies[d], u); @@ -877,7 +928,6 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD } int unit_merge(Unit *u, Unit *other) { - UnitDependency d; const char *other_id = NULL; int r; @@ -894,15 +944,15 @@ int unit_merge(Unit *u, Unit *other) { if (u->type != other->type) return -EINVAL; - if (!u->instance != !other->instance) - return -EINVAL; - if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ return -EEXIST; if (!IN_SET(other->load_state, UNIT_STUB, UNIT_NOT_FOUND)) return -EEXIST; + if (!streq_ptr(u->instance, other->instance)) + return -EINVAL; + if (other->job) return -EEXIST; @@ -916,7 +966,7 @@ int unit_merge(Unit *u, Unit *other) { other_id = strdupa(other->id); /* Make reservations to ensure merge_dependencies() won't fail */ - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { r = reserve_dependencies(u, other, d); /* * We don't rollback reservations if we fail. We don't have @@ -936,7 +986,7 @@ int unit_merge(Unit *u, Unit *other) { unit_ref_set(other->refs_by_target, other->refs_by_target->source, u); /* Merge dependencies */ - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) merge_dependencies(u, other, other_id, d); other->load_state = UNIT_MERGED; @@ -1038,6 +1088,16 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { if (!MANAGER_IS_SYSTEM(u->manager)) return 0; + /* For the following three directory types we need write access, and /var/ is possibly on the root + * fs. Hence order after systemd-remount-fs.service, to ensure things are writable. */ + if (!strv_isempty(c->directories[EXEC_DIRECTORY_STATE].paths) || + !strv_isempty(c->directories[EXEC_DIRECTORY_CACHE].paths) || + !strv_isempty(c->directories[EXEC_DIRECTORY_LOGS].paths)) { + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; + } + if (c->private_tmp) { const char *p; @@ -1052,14 +1112,21 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } + if (c->root_image) { + /* We need to wait for /dev/loopX to appear when doing RootImage=, hence let's add an + * implicit dependency on udev */ + + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_UDEVD_SERVICE, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; + } + if (!IN_SET(c->std_output, EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE, - EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE, - EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) && + EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE) && !IN_SET(c->std_error, EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE, - EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE, - EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) && + EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE) && !c->log_namespace) return 0; @@ -1154,7 +1221,6 @@ 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; - UnitDependency d; Iterator i; const char *prefix2; char timestamp[5][FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX]; @@ -1174,9 +1240,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s-> Unit %s:\n", prefix, u->id); - SET_FOREACH(t, u->names, i) - if (!streq(t, u->id)) - fprintf(f, "%s\tAlias: %s\n", prefix, t); + SET_FOREACH(t, u->aliases, i) + fprintf(f, "%s\tAlias: %s\n", prefix, t); fprintf(f, "%s\tDescription: %s\n" @@ -1310,7 +1375,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->assert_timestamp.realtime)), prefix, yes_no(u->assert_result)); - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { UnitDependencyInfo di; Unit *other; @@ -1455,7 +1520,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) { } static int unit_add_slice_dependencies(Unit *u) { - UnitDependencyMask mask; assert(u); if (!UNIT_HAS_CGROUP_CONTEXT(u)) @@ -1464,7 +1528,7 @@ static int unit_add_slice_dependencies(Unit *u) { /* Slice units are implicitly ordered against their parent slices (as this relationship is encoded in the name), while all other units are ordered based on configuration (as in their case Slice= configures the relationship). */ - mask = u->type == UNIT_SLICE ? UNIT_DEPENDENCY_IMPLICIT : UNIT_DEPENDENCY_FILE; + UnitDependencyMask mask = u->type == UNIT_SLICE ? UNIT_DEPENDENCY_IMPLICIT : UNIT_DEPENDENCY_FILE; if (UNIT_ISSET(u->slice)) return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true, mask); @@ -1527,7 +1591,6 @@ static int unit_add_mount_dependencies(Unit *u) { static int unit_add_startup_units(Unit *u) { CGroupContext *c; - int r; c = unit_get_cgroup_context(u); if (!c) @@ -1538,11 +1601,7 @@ static int unit_add_startup_units(Unit *u) { c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) return 0; - r = set_ensure_allocated(&u->manager->startup_units, NULL); - if (r < 0) - return r; - - return set_put(u->manager->startup_units, u); + return set_ensure_put(&u->manager->startup_units, NULL, u); } int unit_load(Unit *u) { @@ -1623,6 +1682,11 @@ fail: UNIT_ERROR; u->load_error = r; + /* Record the last time we tried to load the unit, so that if the cache gets updated between now + * and the next time an attempt is made to load this unit, we know we need to check again */ + if (u->load_state == UNIT_NOT_FOUND) + u->fragment_loadtime = now(CLOCK_REALTIME); + unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); @@ -1651,24 +1715,50 @@ static int log_unit_internal(void *userdata, int level, int error, const char *f } static bool unit_test_condition(Unit *u) { + _cleanup_strv_free_ char **env = NULL; + int r; + assert(u); dual_timestamp_get(&u->condition_timestamp); - u->condition_result = condition_test_list(u->conditions, condition_type_to_string, log_unit_internal, u); + + r = manager_get_effective_environment(u->manager, &env); + if (r < 0) { + log_unit_error_errno(u, r, "Failed to determine effective environment: %m"); + u->condition_result = CONDITION_ERROR; + } else + u->condition_result = condition_test_list( + u->conditions, + env, + condition_type_to_string, + log_unit_internal, + u); unit_add_to_dbus_queue(u); - return u->condition_result; } static bool unit_test_assert(Unit *u) { + _cleanup_strv_free_ char **env = NULL; + int r; + assert(u); dual_timestamp_get(&u->assert_timestamp); - u->assert_result = condition_test_list(u->asserts, assert_type_to_string, log_unit_internal, u); + + r = manager_get_effective_environment(u->manager, &env); + if (r < 0) { + log_unit_error_errno(u, r, "Failed to determine effective environment: %m"); + u->assert_result = CONDITION_ERROR; + } else + u->assert_result = condition_test_list( + u->asserts, + env, + assert_type_to_string, + log_unit_internal, + u); unit_add_to_dbus_queue(u); - return u->assert_result; } @@ -1823,6 +1913,7 @@ int unit_start(Unit *u) { * waits for a holdoff timer to elapse before it will start again. */ unit_add_to_dbus_queue(u); + unit_cgroup_freezer_action(u, FREEZER_THAW); return UNIT_VTABLE(u)->start(u); } @@ -1875,6 +1966,7 @@ int unit_stop(Unit *u) { return -EBADR; unit_add_to_dbus_queue(u); + unit_cgroup_freezer_action(u, FREEZER_THAW); return UNIT_VTABLE(u)->stop(u); } @@ -1931,6 +2023,8 @@ int unit_reload(Unit *u) { return 0; } + unit_cgroup_freezer_action(u, FREEZER_THAW); + return UNIT_VTABLE(u)->reload(u); } @@ -2156,7 +2250,7 @@ static int unit_log_resources(Unit *u) { struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4]; bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false; _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL; - int log_level = LOG_DEBUG; /* May be raised if resources consumed over a treshold */ + int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */ size_t n_message_parts = 0, n_iovec = 0; char* message_parts[1 + 2 + 2 + 1], *t; nsec_t nsec = NSEC_INFINITY; @@ -2837,13 +2931,13 @@ bool unit_job_is_applicable(Unit *u, JobType j) { case JOB_START: case JOB_NOP: /* Note that we don't check unit_can_start() here. That's because .device units and suchlike are not - * startable by us but may appear due to external events, and it thus makes sense to permit enqueing + * startable by us but may appear due to external events, and it thus makes sense to permit enqueuing * jobs for it. */ return true; case JOB_STOP: /* Similar as above. However, perpetual units can never be stopped (neither explicitly nor due to - * external events), hence it makes no sense to permit enqueing such a request either. */ + * external events), hence it makes no sense to permit enqueuing such a request either. */ return !u->perpetual; case JOB_RESTART: @@ -3121,6 +3215,43 @@ char *unit_dbus_path_invocation_id(Unit *u) { return unit_dbus_path_from_name(u->invocation_id_string); } +static int unit_set_invocation_id(Unit *u, sd_id128_t id) { + int r; + + assert(u); + + /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */ + + if (sd_id128_equal(u->invocation_id, id)) + return 0; + + if (!sd_id128_is_null(u->invocation_id)) + (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u); + + if (sd_id128_is_null(id)) { + r = 0; + goto reset; + } + + r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops); + if (r < 0) + goto reset; + + u->invocation_id = id; + sd_id128_to_string(id, u->invocation_id_string); + + r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u); + if (r < 0) + goto reset; + + return 0; + +reset: + u->invocation_id = SD_ID128_NULL; + u->invocation_id_string[0] = 0; + return r; +} + int unit_set_slice(Unit *u, Unit *slice) { assert(u); assert(slice); @@ -3471,6 +3602,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (!sd_id128_is_null(u->invocation_id)) (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id)); + (void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u))); + bus_track_serialize(u->bus_track, f, "ref"); for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) { @@ -3779,6 +3912,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m"); } + continue; + } else if (streq(l, "freezer-state")) { + FreezerState s; + + s = freezer_state_from_string(v); + if (s < 0) + log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v); + else + u->freezer_state = s; + continue; } @@ -4241,7 +4384,8 @@ int unit_get_unit_file_preset(Unit *u) { u->unit_file_preset = unit_file_query_preset( u->manager->unit_file_scope, NULL, - basename(u->fragment_path)); + basename(u->fragment_path), + NULL); return u->unit_file_preset; } @@ -4385,24 +4529,27 @@ int unit_patch_contexts(Unit *u) { if (ec->root_image && (cc->device_policy != CGROUP_DEVICE_POLICY_AUTO || cc->device_allow)) { + const char *p; /* When RootImage= is specified, the following devices are touched. */ - r = cgroup_add_device_allow(cc, "/dev/loop-control", "rw"); - if (r < 0) - return r; + FOREACH_STRING(p, "/dev/loop-control", "/dev/mapper/control") { + r = cgroup_add_device_allow(cc, p, "rw"); + if (r < 0) + return r; + } + FOREACH_STRING(p, "block-loop", "block-blkext", "block-device-mapper") { + r = cgroup_add_device_allow(cc, p, "rwm"); + if (r < 0) + return r; + } - r = cgroup_add_device_allow(cc, "block-loop", "rwm"); - if (r < 0) - return r; - - r = cgroup_add_device_allow(cc, "block-blkext", "rwm"); - if (r < 0) - return r; - - /* Make sure "block-loop" can be resolved, i.e. make sure "loop" shows up in /proc/devices */ - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "modprobe@loop.service", true, UNIT_DEPENDENCY_FILE); - if (r < 0) - return r; + /* Make sure "block-loop" can be resolved, i.e. make sure "loop" shows up in /proc/devices. + * Same for mapper and verity. */ + FOREACH_STRING(p, "modprobe@loop.service", "modprobe@dm_mod.service", "modprobe@dm_verity.service") { + r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, p, true, UNIT_DEPENDENCY_FILE); + if (r < 0) + return r; + } } if (ec->protect_clock) { @@ -4633,7 +4780,7 @@ int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const ch /* Make sure the drop-in dir is registered in our path cache. This way we don't need to stupidly * recreate the cache after every drop-in we write. */ if (u->manager->unit_path_cache) { - r = set_put_strdup(u->manager->unit_path_cache, p); + r = set_put_strdup(&u->manager->unit_path_cache, p); if (r < 0) return r; } @@ -4874,11 +5021,11 @@ int unit_kill_context( if (!pid_set) return -ENOMEM; - cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, - SIGHUP, - CGROUP_IGNORE_SELF, - pid_set, - NULL, NULL); + (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, + SIGHUP, + CGROUP_IGNORE_SELF, + pid_set, + NULL, NULL); } } } @@ -5247,43 +5394,6 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) { unit_add_to_dbus_queue(u); } -int unit_set_invocation_id(Unit *u, sd_id128_t id) { - int r; - - assert(u); - - /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */ - - if (sd_id128_equal(u->invocation_id, id)) - return 0; - - if (!sd_id128_is_null(u->invocation_id)) - (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u); - - if (sd_id128_is_null(id)) { - r = 0; - goto reset; - } - - r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops); - if (r < 0) - goto reset; - - u->invocation_id = id; - sd_id128_to_string(id, u->invocation_id_string); - - r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u); - if (r < 0) - goto reset; - - return 0; - -reset: - u->invocation_id = SD_ID128_NULL; - u->invocation_id_string[0] = 0; - return r; -} - int unit_acquire_invocation_id(Unit *u) { sd_id128_t id; int r; @@ -5405,8 +5515,6 @@ static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other, } void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { - UnitDependency d; - assert(u); /* Removes all dependencies u has on other units marked for ownership by 'mask'. */ @@ -5414,7 +5522,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { if (mask == 0) return; - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { bool done; do { @@ -5425,8 +5533,6 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { done = true; HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d], i) { - UnitDependency q; - if ((di.origin_mask & ~mask) == di.origin_mask) continue; di.origin_mask &= ~mask; @@ -5437,7 +5543,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { * all dependency types on the other unit and delete all those which point to us and * have the right mask set. */ - for (q = 0; q < _UNIT_DEPENDENCY_MAX; q++) { + for (UnitDependency q = 0; q < _UNIT_DEPENDENCY_MAX; q++) { UnitDependencyInfo dj; dj.data = hashmap_get(other->dependencies[q], u); @@ -5764,14 +5870,20 @@ int unit_prepare_exec(Unit *u) { return 0; } -static int log_leftover(pid_t pid, int sig, void *userdata) { +static bool ignore_leftover_process(const char *comm) { + return comm && comm[0] == '('; /* Most likely our own helper process (PAM?), ignore */ +} + +int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata) { _cleanup_free_ char *comm = NULL; (void) get_process_comm(pid, &comm); - if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */ + if (ignore_leftover_process(comm)) return 0; + /* During start we print a warning */ + log_unit_warning(userdata, "Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n" "This usually indicates unclean termination of a previous run, or service implementation deficiencies.", @@ -5780,7 +5892,24 @@ static int log_leftover(pid_t pid, int sig, void *userdata) { return 1; } -int unit_warn_leftover_processes(Unit *u) { +int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata) { + _cleanup_free_ char *comm = NULL; + + (void) get_process_comm(pid, &comm); + + if (ignore_leftover_process(comm)) + return 0; + + /* During stop we only print an informational message */ + + log_unit_info(userdata, + "Unit process " PID_FMT " (%s) remains running after unit stopped.", + pid, strna(comm)); + + return 1; +} + +int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func) { assert(u); (void) unit_pick_cgroup_path(u); @@ -5788,7 +5917,7 @@ int unit_warn_leftover_processes(Unit *u) { if (!u->cgroup_path) return 0; - return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u); + return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_func, u); } bool unit_needs_console(Unit *u) { @@ -5826,7 +5955,7 @@ const char *unit_label_path(const Unit *u) { return NULL; /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */ - if (path_equal(p, "/dev/null")) + if (null_or_empty_path(p) > 0) return NULL; return p; @@ -6050,6 +6179,80 @@ int unit_can_clean(Unit *u, ExecCleanMask *ret) { return UNIT_VTABLE(u)->can_clean(u, ret); } +bool unit_can_freeze(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->can_freeze) + return UNIT_VTABLE(u)->can_freeze(u); + + return UNIT_VTABLE(u)->freeze; +} + +void unit_frozen(Unit *u) { + assert(u); + + u->freezer_state = FREEZER_FROZEN; + + bus_unit_send_pending_freezer_message(u); +} + +void unit_thawed(Unit *u) { + assert(u); + + u->freezer_state = FREEZER_RUNNING; + + bus_unit_send_pending_freezer_message(u); +} + +static int unit_freezer_action(Unit *u, FreezerAction action) { + UnitActiveState s; + int (*method)(Unit*); + int r; + + assert(u); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + + method = action == FREEZER_FREEZE ? UNIT_VTABLE(u)->freeze : UNIT_VTABLE(u)->thaw; + if (!method || !cg_freezer_supported()) + return -EOPNOTSUPP; + + if (u->job) + return -EBUSY; + + if (u->load_state != UNIT_LOADED) + return -EHOSTDOWN; + + s = unit_active_state(u); + if (s != UNIT_ACTIVE) + return -EHOSTDOWN; + + if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) + return -EALREADY; + + r = method(u); + if (r <= 0) + return r; + + return 1; +} + +int unit_freeze(Unit *u) { + return unit_freezer_action(u, FREEZER_FREEZE); +} + +int unit_thaw(Unit *u) { + return unit_freezer_action(u, FREEZER_THAW); +} + +/* Wrappers around low-level cgroup freezer operations common for service and scope units */ +int unit_freeze_vtable_common(Unit *u) { + return unit_cgroup_freezer_action(u, FREEZER_FREEZE); +} + +int unit_thaw_vtable_common(Unit *u) { + return unit_cgroup_freezer_action(u, FREEZER_THAW); +} + static const char* const collect_mode_table[_COLLECT_MODE_MAX] = { [COLLECT_INACTIVE] = "inactive", [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed", diff --git a/src/core/unit.h b/src/core/unit.h index eb0a34477..d5e4c6598 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -114,10 +114,13 @@ typedef struct Unit { UnitLoadState load_state; Unit *merged_into; - char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ + FreezerState freezer_state; + sd_bus_message *pending_freezer_message; + + char *id; /* The one special name that we use for identification */ char *instance; - Set *names; + Set *aliases; /* All the other names. */ /* For each dependency type we maintain a Hashmap whose key is the Unit* object, and the value encodes why the * dependency exists, using the UnitDependencyInfo type */ @@ -133,6 +136,7 @@ typedef struct Unit { char *source_path; /* if converted, the source file */ char **dropin_paths; + usec_t fragment_loadtime; usec_t fragment_mtime; usec_t source_mtime; usec_t dropin_mtime; @@ -483,6 +487,11 @@ typedef struct UnitVTable { /* Clear out the various runtime/state/cache/logs/configuration data */ int (*clean)(Unit *u, ExecCleanMask m); + /* Freeze the unit */ + int (*freeze)(Unit *u); + int (*thaw)(Unit *u); + bool (*can_freeze)(Unit *u); + /* Return which kind of data can be cleaned */ int (*can_clean)(Unit *u, ExecCleanMask *ret); @@ -531,7 +540,7 @@ typedef struct UnitVTable { void (*notify_cgroup_oom)(Unit *u); /* Called whenever a process of this unit sends us a message */ - void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds); + void (*notify_message)(Unit *u, const struct ucred *ucred, char * const *tags, FDSet *fds); /* Called whenever a name this Unit registered for comes or goes away. */ void (*bus_name_owner_change)(Unit *u, const char *new_owner); @@ -592,9 +601,6 @@ typedef struct UnitVTable { * of this type will immediately fail. */ bool (*supported)(void); - /* The bus vtable */ - const sd_bus_vtable *bus_vtable; - /* The strings to print in status messages */ UnitStatusMessageFormats status_message_formats; @@ -692,6 +698,8 @@ const char *unit_status_string(Unit *u) _pure_; bool unit_has_name(const Unit *u, const char *name); UnitActiveState unit_active_state(Unit *u); +FreezerState unit_freezer_state(Unit *u); +int unit_freezer_state_kernel(Unit *u, FreezerState *ret); const char* unit_sub_state_to_string(Unit *u); @@ -822,7 +830,6 @@ void unit_unref_uid_gid(Unit *u, bool destroy_now); void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid); -int unit_set_invocation_id(Unit *u, sd_id128_t id); int unit_acquire_invocation_id(Unit *u); bool unit_shall_confirm_spawn(Unit *u); @@ -839,7 +846,9 @@ void unit_unlink_state_files(Unit *u); int unit_prepare_exec(Unit *u); -int unit_warn_leftover_processes(Unit *u); +int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata); +int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata); +int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func); bool unit_needs_console(Unit *u); @@ -875,6 +884,16 @@ void unit_destroy_runtime_directory(Unit *u, const ExecContext *context); int unit_clean(Unit *u, ExecCleanMask mask); int unit_can_clean(Unit *u, ExecCleanMask *ret_mask); +bool unit_can_freeze(Unit *u); +int unit_freeze(Unit *u); +void unit_frozen(Unit *u); + +int unit_thaw(Unit *u); +void unit_thawed(Unit *u); + +int unit_freeze_vtable_common(Unit *u); +int unit_thaw_vtable_common(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/core/user.conf.in b/src/core/user.conf.in index 95a162e0f..bbe06319c 100644 --- a/src/core/user.conf.in +++ b/src/core/user.conf.in @@ -15,6 +15,7 @@ #LogTarget=console #LogColor=yes #LogLocation=no +#LogTime=no #SystemCallArchitectures= #TimerSlackNSec= #StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@ diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 6b5124030..8b052dac2 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -155,11 +155,14 @@ static int parse_config(void) { {} }; - return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf", - CONF_PATHS_NULSTR("systemd/coredump.conf.d"), - "Coredump\0", - config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/coredump.conf", + CONF_PATHS_NULSTR("systemd/coredump.conf.d"), + "Coredump\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); } static uint64_t storage_size_max(void) { @@ -174,38 +177,18 @@ static uint64_t storage_size_max(void) { static int fix_acl(int fd, uid_t uid) { #if HAVE_ACL - _cleanup_(acl_freep) acl_t acl = NULL; - acl_entry_t entry; - acl_permset_t permset; int r; assert(fd >= 0); + assert(uid_is_valid(uid)); if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) return 0; - /* Make sure normal users can read (but not write or delete) - * their own coredumps */ - - acl = acl_get_fd(fd); - if (!acl) - return log_error_errno(errno, "Failed to get ACL: %m"); - - if (acl_create_entry(&acl, &entry) < 0 || - acl_set_tag_type(entry, ACL_USER) < 0 || - acl_set_qualifier(entry, &uid) < 0) - return log_error_errno(errno, "Failed to patch ACL: %m"); - - if (acl_get_permset(entry, &permset) < 0 || - acl_add_perm(permset, ACL_READ) < 0) - return log_warning_errno(errno, "Failed to patch ACL: %m"); - - r = calc_acl_mask_if_needed(&acl); + /* Make sure normal users can read (but not write or delete) their own coredumps */ + r = add_acls_for_user(fd, uid); if (r < 0) - return log_warning_errno(r, "Failed to patch ACL: %m"); - - if (acl_set_fd(fd, acl) < 0) - return log_error_errno(errno, "Failed to apply ACL: %m"); + return log_error_errno(r, "Failed to adjust ACL of coredump: %m"); #endif return 0; @@ -420,7 +403,7 @@ static int save_external_coredump( goto fail; } -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION /* If we will remove the coredump anyway, do not compress. */ if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) { @@ -560,7 +543,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) { FOREACH_DIRENT(dent, proc_fd_dir, return -errno) { _cleanup_fclose_ FILE *fdinfo = NULL; _cleanup_free_ char *fdname = NULL; - int fd; + _cleanup_close_ int fd = -1; r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname); if (r < 0) @@ -574,11 +557,9 @@ static int compose_open_fds(pid_t pid, char **open_fds) { if (fd < 0) continue; - fdinfo = fdopen(fd, "r"); - if (!fdinfo) { - safe_close(fd); + fdinfo = take_fdopen(&fd, "r"); + if (!fdinfo) continue; - } for (;;) { _cleanup_free_ char *line = NULL; @@ -886,10 +867,7 @@ static int process_socket(int fd) { log_debug("Processing coredump received on stdin..."); for (;;) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; struct msghdr mh = { .msg_control = &control, .msg_controllen = sizeof(control), @@ -923,19 +901,11 @@ static int process_socket(int fd) { /* The final zero-length datagram carries the file descriptor and tells us * that we're done. */ if (n == 0) { - struct cmsghdr *cmsg, *found = NULL; + struct cmsghdr *found; free(iovec.iov_base); - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - } - } - + found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); if (!found) { cmsg_close_all(&mh); r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG), diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 4172e8e84..02502528a 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -765,7 +765,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) if (access(filename, R_OK) < 0) return log_error_errno(errno, "File \"%s\" is not readable: %m", filename); - if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { + if (path && !ENDSWITH_SET(filename, ".xz", ".lz4", ".zst")) { *path = TAKE_PTR(filename); return 0; @@ -824,7 +824,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) } if (filename) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION _cleanup_close_ int fdf; fdf = open(filename, O_RDONLY | O_CLOEXEC); @@ -839,8 +839,8 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) goto error; } #else - log_error("Cannot decompress file. Compiled without compression support."); - r = -EOPNOTSUPP; + r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Cannot decompress file. Compiled without compression support."); goto error; #endif } else { @@ -1091,9 +1091,7 @@ static int run(int argc, char *argv[]) { int r, units_active; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); /* The journal merging logic potentially needs a lot of fds. */ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 1deab765f..b93fbb866 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -39,7 +39,7 @@ static bool arg_enabled = true; static bool arg_read_crypttab = true; static const char *arg_crypttab = NULL; static const char *arg_runtime_directory = NULL; -static bool arg_whitelist = false; +static bool arg_allow_list = false; static Hashmap *arg_disks = NULL; static char *arg_default_options = NULL; static char *arg_default_keyfile = NULL; @@ -188,7 +188,11 @@ static int print_dependencies(FILE *f, const char* device_path) { /* None, nothing to do */ return 0; - if (PATH_IN_SET(device_path, "/dev/urandom", "/dev/random", "/dev/hw_random")) { + if (PATH_IN_SET(device_path, + "/dev/urandom", + "/dev/random", + "/dev/hw_random", + "/dev/hwrng")) { /* RNG device, add random dep */ fputs("After=systemd-random-seed.service\n", f); return 0; @@ -202,14 +206,16 @@ static int print_dependencies(FILE *f, const char* device_path) { return 0; if (path_startswith(udev_node, "/dev/")) { - /* We are dealing with a block device, add dependency for correspoding unit */ + /* We are dealing with a block device, add dependency for corresponding unit */ _cleanup_free_ char *unit = NULL; r = unit_name_from_path(udev_node, ".device", &unit); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - fprintf(f, "After=%1$s\nRequires=%1$s\n", unit); + fprintf(f, + "After=%1$s\n" + "Requires=%1$s\n", unit); } else { /* Regular file, add mount dependency */ _cleanup_free_ char *escaped_path = specifier_escape(device_path); @@ -231,18 +237,18 @@ 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, *password_buffer = NULL, + *tmp_fstype = NULL; _cleanup_fclose_ FILE *f = NULL; const char *dmname; - bool noauto, nofail, tmp, swap, netdev, attach_in_initrd; - int r, detached_header, keyfile_can_timeout; + bool noauto, nofail, swap, netdev, attach_in_initrd; + int r, detached_header, keyfile_can_timeout, tmp; assert(name); assert(device); noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); - tmp = fstab_test_option(options, "tmp\0"); swap = fstab_test_option(options, "swap\0"); netdev = fstab_test_option(options, "_netdev\0"); attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0"); @@ -255,6 +261,10 @@ static int create_disk( if (detached_header < 0) return log_error_errno(detached_header, "Failed to parse header= option value: %m"); + tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL); + if (tmp < 0) + return log_error_errno(tmp, "Failed to parse tmp= option value: %m"); + if (tmp && swap) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", @@ -365,14 +375,23 @@ static int create_disk( if (r < 0) return r; - if (tmp) + if (tmp) { + _cleanup_free_ char *tmp_fstype_escaped = NULL; + + if (tmp_fstype) { + tmp_fstype_escaped = specifier_escape(tmp_fstype); + if (!tmp_fstype_escaped) + return log_oom(); + } + fprintf(f, - "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", - name_escaped); + "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs '%s' '/dev/mapper/%s'\n", + tmp_fstype_escaped ?: "ext4", name_escaped); + } if (swap) fprintf(f, - "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", + "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs swap '/dev/mapper/%s'\n", name_escaped); if (keydev) @@ -476,7 +495,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!d) return log_oom(); - d->create = arg_whitelist = true; + d->create = arg_allow_list = true; } else if (streq(key, "luks.options")) { @@ -540,7 +559,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!d) return log_oom(); - d->create = arg_whitelist = true; + d->create = arg_allow_list = true; free_and_replace(d->name, uuid_value); } else @@ -603,7 +622,7 @@ static int add_crypttab_devices(void) { if (uuid) d = hashmap_get(arg_disks, uuid); - if (arg_whitelist && !d) { + if (arg_allow_list && !d) { log_info("Not creating device '%s' because it was not specified on the kernel command line.", name); continue; } diff --git a/src/cryptsetup/cryptsetup-pkcs11.c b/src/cryptsetup/cryptsetup-pkcs11.c index ec9186a6a..642a1b7d1 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.c +++ b/src/cryptsetup/cryptsetup-pkcs11.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "ask-password-api.h" #include "cryptsetup-pkcs11.h" +#include "cryptsetup-util.h" #include "escape.h" #include "fd-util.h" #include "format-util.h" @@ -19,73 +20,6 @@ #include "stat-util.h" #include "strv.h" -#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */ - -static int load_key_file( - const char *key_file, - size_t key_file_size, - uint64_t key_file_offset, - void **ret_encrypted_key, - size_t *ret_encrypted_key_size) { - - _cleanup_(erase_and_freep) char *buffer = NULL; - _cleanup_close_ int fd = -1; - ssize_t n; - int r; - - assert(key_file); - assert(ret_encrypted_key); - assert(ret_encrypted_key_size); - - fd = open(key_file, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m"); - - if (key_file_size == 0) { - struct stat st; - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Failed to stat key file: %m"); - - r = stat_verify_regular(&st); - if (r < 0) - return log_error_errno(r, "Key file is not a regular file: %m"); - - if (st.st_size == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing."); - if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) { - char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX]; - return log_error_errno(SYNTHETIC_ERRNO(ERANGE), - "Key file larger (%s) than allowed maximum size (%s), refusing.", - format_bytes(buf1, sizeof(buf1), st.st_size), - format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX)); - } - - if (key_file_offset >= (uint64_t) st.st_size) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing."); - - key_file_size = st.st_size - key_file_offset; - } - - buffer = malloc(key_file_size); - if (!buffer) - return log_oom(); - - if (key_file_offset > 0) - n = pread(fd, buffer, key_file_size, key_file_offset); - else - n = read(fd, buffer, key_file_size); - if (n < 0) - return log_error_errno(errno, "Failed to read PKCS#11 key file: %m"); - if (n == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing."); - - *ret_encrypted_key = TAKE_PTR(buffer); - *ret_encrypted_key_size = (size_t) n; - - return 0; -} - struct pkcs11_callback_data { const char *friendly_name; usec_t until; @@ -93,11 +27,14 @@ struct pkcs11_callback_data { size_t encrypted_key_size; void *decrypted_key; size_t decrypted_key_size; + bool free_encrypted_key; }; static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) { free(data->decrypted_key); - free(data->encrypted_key); + + if (data->free_encrypted_key) + free(data->encrypted_key); } static int pkcs11_callback( @@ -160,9 +97,11 @@ static int pkcs11_callback( int decrypt_pkcs11_key( const char *friendly_name, const char *pkcs11_uri, - const char *key_file, + const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */ size_t key_file_size, uint64_t key_file_offset, + const void *key_data, /* … or key_data and key_data_size (for literal keys) */ + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { @@ -175,15 +114,24 @@ int decrypt_pkcs11_key( assert(friendly_name); assert(pkcs11_uri); - assert(key_file); + assert(key_file || key_data); assert(ret_decrypted_key); assert(ret_decrypted_key_size); /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */ - r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size); - if (r < 0) - return r; + if (key_data) { + data.encrypted_key = (void*) key_data; + data.encrypted_key_size = key_data_size; + + data.free_encrypted_key = false; + } else { + r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size); + if (r < 0) + return r; + + data.free_encrypted_key = true; + } r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data); if (r < 0) diff --git a/src/cryptsetup/cryptsetup-pkcs11.h b/src/cryptsetup/cryptsetup-pkcs11.h index 264ccb66b..af2487e75 100644 --- a/src/cryptsetup/cryptsetup-pkcs11.h +++ b/src/cryptsetup/cryptsetup-pkcs11.h @@ -14,6 +14,8 @@ int decrypt_pkcs11_key( const char *key_file, size_t key_file_size, uint64_t key_file_offset, + const void *key_data, + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size); @@ -26,6 +28,8 @@ static inline int decrypt_pkcs11_key( const char *key_file, size_t key_file_size, uint64_t key_file_offset, + const void *key_data, + size_t key_data_size, usec_t until, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { diff --git a/src/cryptsetup/cryptsetup-util.c b/src/cryptsetup/cryptsetup-util.c new file mode 100644 index 000000000..8ae70a5d1 --- /dev/null +++ b/src/cryptsetup/cryptsetup-util.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "cryptsetup-util.h" +#include "fd-util.h" +#include "format-util.h" +#include "memory-util.h" +#include "path-util.h" +#include "stat-util.h" +#include "strv.h" + +#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */ + +int load_key_file( + const char *key_file, + char **search_path, + size_t key_file_size, + uint64_t key_file_offset, + void **ret_key, + size_t *ret_key_size) { + + _cleanup_(erase_and_freep) char *buffer = NULL; + _cleanup_free_ char *discovered_path = NULL; + _cleanup_close_ int fd = -1; + ssize_t n; + int r; + + assert(key_file); + assert(ret_key); + assert(ret_key_size); + + if (strv_isempty(search_path) || path_is_absolute(key_file)) { + fd = open(key_file, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to load key file '%s': %m", key_file); + } else { + char **i; + + STRV_FOREACH(i, search_path) { + _cleanup_free_ char *joined; + + joined = path_join(*i, key_file); + if (!joined) + return log_oom(); + + fd = open(joined, O_RDONLY|O_CLOEXEC); + if (fd >= 0) { + discovered_path = TAKE_PTR(joined); + break; + } + if (errno != ENOENT) + return log_error_errno(errno, "Failed to load key file '%s': %m", joined); + } + + if (!discovered_path) { + /* Search path supplied, but file not found, report by returning NULL, but not failing */ + *ret_key = NULL; + *ret_key_size = 0; + return 0; + } + + assert(fd >= 0); + key_file = discovered_path; + } + + if (key_file_size == 0) { + struct stat st; + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat key file '%s': %m", key_file); + + r = stat_verify_regular(&st); + if (r < 0) + return log_error_errno(r, "Key file is not a regular file: %m"); + + if (st.st_size == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing."); + if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) { + char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX]; + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), + "Key file larger (%s) than allowed maximum size (%s), refusing.", + format_bytes(buf1, sizeof(buf1), st.st_size), + format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX)); + } + + if (key_file_offset >= (uint64_t) st.st_size) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing."); + + key_file_size = st.st_size - key_file_offset; + } + + buffer = malloc(key_file_size); + if (!buffer) + return log_oom(); + + if (key_file_offset > 0) + n = pread(fd, buffer, key_file_size, key_file_offset); + else + n = read(fd, buffer, key_file_size); + if (n < 0) + return log_error_errno(errno, "Failed to read key file '%s': %m", key_file); + if (n == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing."); + + *ret_key = TAKE_PTR(buffer); + *ret_key_size = (size_t) n; + + return 1; +} diff --git a/src/cryptsetup/cryptsetup-util.h b/src/cryptsetup/cryptsetup-util.h new file mode 100644 index 000000000..7bb78227d --- /dev/null +++ b/src/cryptsetup/cryptsetup-util.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +int load_key_file( + const char *key_file, + char **search_path, + size_t key_file_size, + uint64_t key_file_offset, + void **ret_key, + size_t *ret_key_size); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 860b29b3b..6d3f842db 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -13,13 +13,16 @@ #include "ask-password-api.h" #include "crypt-util.h" #include "cryptsetup-pkcs11.h" +#include "cryptsetup-util.h" #include "device-util.h" #include "escape.h" #include "fileio.h" +#include "fs-util.h" #include "fstab-util.h" #include "hexdecoct.h" #include "log.h" #include "main-func.h" +#include "memory-util.h" #include "mount-util.h" #include "nulstr-util.h" #include "parse-util.h" @@ -35,13 +38,15 @@ #define CRYPT_SECTOR_SIZE 512 #define CRYPT_MAX_SECTOR_SIZE 4096 -static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */ +static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT, CRYPT_BITLK or CRYPT_PLAIN */ static char *arg_cipher = NULL; static unsigned arg_key_size = 0; static unsigned arg_sector_size = CRYPT_SECTOR_SIZE; static int arg_key_slot = CRYPT_ANY_SLOT; static unsigned arg_keyfile_size = 0; static uint64_t arg_keyfile_offset = 0; +static bool arg_keyfile_erase = false; +static bool arg_try_empty_password = false; static char *arg_hash = NULL; static char *arg_header = NULL; static unsigned arg_tries = 3; @@ -67,12 +72,13 @@ STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep); /* Options Debian's crypttab knows we don't: - precheck= check= checkargs= - noearly= - loud= + noearly + loud + quiet keyscript= + initramfs */ static int parse_one_option(const char *option) { @@ -126,7 +132,8 @@ static int parse_one_option(const char *option) { return 0; } - } else if ((val = startswith(option, "key-slot="))) { + } else if ((val = startswith(option, "key-slot=")) || + (val = startswith(option, "keyslot="))) { arg_type = ANY_LUKS; r = safe_atoi(val, &arg_key_slot); @@ -160,7 +167,20 @@ static int parse_one_option(const char *option) { return 0; } - } else if ((val = startswith(option, "hash="))) { + } else if ((val = startswith(option, "keyfile-erase="))) { + + r = parse_boolean(val); + if (r < 0) { + log_error_errno(r, "Failed to parse %s, ignoring: %m", option); + return 0; + } + + arg_keyfile_erase = r; + + } else if (streq(option, "keyfile-erase")) + arg_keyfile_erase = true; + + else if ((val = startswith(option, "hash="))) { r = free_and_strdup(&arg_hash, val); if (r < 0) return log_oom(); @@ -200,18 +220,24 @@ static int parse_one_option(const char *option) { arg_submit_from_crypt_cpus = true; else if (streq(option, "luks")) arg_type = ANY_LUKS; +/* since cryptsetup 2.3.0 (Feb 2020) */ +#ifdef CRYPT_BITLK + else if (streq(option, "bitlk")) + arg_type = CRYPT_BITLK; +#endif else if (streq(option, "tcrypt")) arg_type = CRYPT_TCRYPT; - else if (streq(option, "tcrypt-hidden")) { + else if (STR_IN_SET(option, "tcrypt-hidden", "tcrypthidden")) { arg_type = CRYPT_TCRYPT; arg_tcrypt_hidden = true; } else if (streq(option, "tcrypt-system")) { arg_type = CRYPT_TCRYPT; arg_tcrypt_system = true; - } else if (streq(option, "tcrypt-veracrypt")) { + } else if (STR_IN_SET(option, "tcrypt-veracrypt", "veracrypt")) { arg_type = CRYPT_TCRYPT; arg_tcrypt_veracrypt = true; - } else if (STR_IN_SET(option, "plain", "swap", "tmp")) + } else if (STR_IN_SET(option, "plain", "swap", "tmp") || + startswith(option, "tmp=")) arg_type = CRYPT_PLAIN; else if ((val = startswith(option, "timeout="))) { @@ -242,7 +268,20 @@ static int parse_one_option(const char *option) { if (r < 0) return log_oom(); - } else if (!streq(option, "x-initrd.attach")) + } else if ((val = startswith(option, "try-empty-password="))) { + + r = parse_boolean(val); + if (r < 0) { + log_error_errno(r, "Failed to parse %s, ignoring: %m", option); + return 0; + } + + arg_try_empty_password = r; + + } else if (streq(option, "try-empty-password")) + arg_try_empty_password = true; + + else if (!streq(option, "x-initrd.attach")) log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option); return 0; @@ -440,6 +479,8 @@ static int attach_tcrypt( struct crypt_device *cd, const char *name, const char *key_file, + const void *key_data, + size_t key_data_size, char **passwords, uint32_t flags) { @@ -453,7 +494,7 @@ static int attach_tcrypt( assert(cd); assert(name); - assert(key_file || (passwords && passwords[0])); + assert(key_file || key_data || !strv_isempty(passwords)); if (arg_pkcs11_uri) /* Ask for a regular password */ @@ -469,22 +510,33 @@ static int attach_tcrypt( if (arg_tcrypt_veracrypt) params.flags |= CRYPT_TCRYPT_VERA_MODES; - if (key_file) { - r = read_one_line_file(key_file, &passphrase); - if (r < 0) { - log_error_errno(r, "Failed to read password file '%s': %m", key_file); - return -EAGAIN; /* log with the actual error, but return EAGAIN */ - } + if (key_data) { + params.passphrase = key_data; + params.passphrase_size = key_data_size; + } else { + if (key_file) { + r = read_one_line_file(key_file, &passphrase); + if (r < 0) { + log_error_errno(r, "Failed to read password file '%s': %m", key_file); + return -EAGAIN; /* log with the actual error, but return EAGAIN */ + } - params.passphrase = passphrase; - } else - params.passphrase = passwords[0]; - params.passphrase_size = strlen(params.passphrase); + params.passphrase = passphrase; + } else + params.passphrase = passwords[0]; + + params.passphrase_size = strlen(params.passphrase); + } r = crypt_load(cd, CRYPT_TCRYPT, ¶ms); if (r < 0) { - if (key_file && r == -EPERM) { - log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file); + if (r == -EPERM) { + if (key_data) + log_error_errno(r, "Failed to activate using discovered key. (Key not correct?)"); + + if (key_file) + log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file); + return -EAGAIN; /* log the actual error, but return EAGAIN */ } @@ -498,10 +550,12 @@ static int attach_tcrypt( return 0; } -static int attach_luks_or_plain( +static int attach_luks_or_plain_or_bitlk( struct crypt_device *cd, const char *name, const char *key_file, + const void *key_data, + size_t key_data_size, char **passwords, uint32_t flags, usec_t until) { @@ -571,7 +625,7 @@ static int attach_luks_or_plain( _cleanup_free_ char *friendly = NULL; size_t decrypted_key_size = 0; - if (!key_file) + if (!key_file && !key_data) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing."); friendly = friendly_disk_name(crypt_get_device_name(cd), name); @@ -584,8 +638,8 @@ static int attach_luks_or_plain( r = decrypt_pkcs11_key( friendly, arg_pkcs11_uri, - key_file, - arg_keyfile_size, arg_keyfile_offset, + key_file, arg_keyfile_size, arg_keyfile_offset, + key_data, key_data_size, until, &decrypted_key, &decrypted_key_size); if (r >= 0) @@ -620,7 +674,7 @@ static int attach_luks_or_plain( return log_error_errno(r, "Failed to start device monitor: %m"); log_notice("Security token %s not present for unlocking volume %s, please plug it in.", - arg_pkcs11_uri, friendly); + arg_pkcs11_uri, friendly); /* Let's immediately rescan in case the token appeared in the time we needed * to create and configure the monitor */ @@ -668,6 +722,18 @@ static int attach_luks_or_plain( if (r < 0) return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m"); + } else if (key_data) { + if (pass_volume_key) + r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags); + else + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags); + if (r == -EPERM) { + log_error_errno(r, "Failed to activate. (Key incorrect?)"); + return -EAGAIN; /* Log actual error, but return EAGAIN */ + } + if (r < 0) + return log_error_errno(r, "Failed to activate: %m"); + } else if (key_file) { r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); if (r == -EPERM) { @@ -748,6 +814,17 @@ static uint32_t determine_flags(void) { return flags; } +static void remove_and_erasep(const char **p) { + int r; + + if (!*p) + return; + + r = unlinkat_deallocate(AT_FDCWD, *p, UNLINK_ERASE); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Unable to erase key file '%s', ignoring: %m", *p); +} + static int run(int argc, char *argv[]) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; @@ -774,13 +851,19 @@ static int run(int argc, char *argv[]) { unsigned tries; usec_t until; crypt_status_info status; + _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL; const char *key_file = NULL; + _cleanup_(erase_and_freep) void *key_data = NULL; + size_t key_data_size = 0; /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ if (argc < 4) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); + if (!filename_is_valid(argv[2])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]); + if (argc >= 5 && !STR_IN_SET(argv[4], "", "-", "none")) { if (path_is_absolute(argv[4])) key_file = argv[4]; @@ -797,6 +880,24 @@ static int run(int argc, char *argv[]) { /* A delicious drop of snake oil */ (void) mlockall(MCL_FUTURE); + if (!key_file) { + const char *fn; + + /* If a key file is not explicitly specified, search for a key in a well defined + * search path, and load it. */ + + fn = strjoina(argv[2], ".key"); + r = load_key_file(fn, + STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"), + 0, 0, /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */ + &key_data, &key_data_size); + if (r < 0) + return r; + if (r > 0) + log_debug("Automatically discovered key for volume '%s'.", argv[2]); + } else if (arg_keyfile_erase) + destroy_key_file = key_file; /* let's get this baby erased when we leave */ + if (arg_header) { log_debug("LUKS header: %s", arg_header); r = crypt_init(&cd, arg_header); @@ -831,8 +932,8 @@ static int run(int argc, char *argv[]) { log_warning("Key file %s is world-readable. This is not a good idea!", key_file); } - if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) { - r = crypt_load(cd, CRYPT_LUKS, NULL); + if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2)) { + r = crypt_load(cd, !arg_type || streq(arg_type, ANY_LUKS) ? CRYPT_LUKS : arg_type, NULL); if (r < 0) return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd)); @@ -843,7 +944,7 @@ static int run(int argc, char *argv[]) { } /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */ - if (!key_file) { + if (!key_file && !key_data) { r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags); if (r >= 0) { log_debug("Volume %s activated with LUKS token id %i.", argv[2], r); @@ -854,29 +955,65 @@ static int run(int argc, char *argv[]) { } } +/* since cryptsetup 2.3.0 (Feb 2020) */ +#ifdef CRYPT_BITLK + if (streq_ptr(arg_type, CRYPT_BITLK)) { + r = crypt_load(cd, CRYPT_BITLK, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load Bitlocker superblock on device %s: %m", crypt_get_device_name(cd)); + } +#endif + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; - if (!key_file && !arg_pkcs11_uri) { - r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); - if (r == -EAGAIN) - continue; - if (r < 0) - return r; + /* When we were able to acquire multiple keys, let's always process them in this order: + * + * 1. A key acquired via PKCS#11 token + * 2. The discovered key: i.e. key_data + key_data_size + * 3. The configured key: i.e. key_file + arg_keyfile_offset + arg_keyfile_size + * 4. The empty password, in case arg_try_empty_password is set + * 5. We enquire the user for a password + */ + + if (!key_file && !key_data && !arg_pkcs11_uri) { + + if (arg_try_empty_password) { + /* Hmm, let's try an empty password now, but only once */ + arg_try_empty_password = false; + + key_data = strdup(""); + if (!key_data) + return log_oom(); + + key_data_size = 0; + } else { + /* Ask the user for a passphrase only as last resort, if we have + * nothing else to check for */ + + r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); + if (r == -EAGAIN) + continue; + if (r < 0) + return r; + } } if (streq_ptr(arg_type, CRYPT_TCRYPT)) - r = attach_tcrypt(cd, argv[2], key_file, passwords, flags); + r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags); else - r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until); + r = attach_luks_or_plain_or_bitlk(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until); if (r >= 0) break; if (r != -EAGAIN) return r; - /* Passphrase not correct? Let's try again! */ + /* Key not correct? Let's try again! */ + key_file = NULL; - arg_pkcs11_uri = NULL; + key_data = erase_and_free(key_data); + key_data_size = 0; + arg_pkcs11_uri = mfree(arg_pkcs11_uri); } if (arg_tries != 0 && tries >= arg_tries) @@ -884,6 +1021,9 @@ static int run(int argc, char *argv[]) { } else if (streq(argv[1], "detach")) { + if (!filename_is_valid(argv[2])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]); + r = crypt_init_by_name(&cd, argv[2]); if (r == -ENODEV) { log_info("Volume %s already inactive.", argv[2]); diff --git a/src/delta/delta.c b/src/delta/delta.c index 2d80d3a66..29e512037 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -643,9 +643,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r, k, n_found = 0; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c index 7fb80ca13..4f38de8e2 100644 --- a/src/detect-virt/detect-virt.c +++ b/src/detect-virt/detect-virt.c @@ -128,9 +128,7 @@ static int run(int argc, char *argv[]) { * to detect whether we are being run in a virtualized * environment or not */ - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index e1418419f..66ac63840 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -12,6 +12,7 @@ #include "loop-util.h" #include "main-func.h" #include "parse-util.h" +#include "path-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -25,21 +26,34 @@ static const char *arg_image = NULL; static const char *arg_path = NULL; static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK; static void *arg_root_hash = NULL; +static char *arg_verity_data = NULL; static size_t arg_root_hash_size = 0; +static char *arg_root_hash_sig_path = NULL; +static void *arg_root_hash_sig = NULL; +static size_t arg_root_hash_sig_size = 0; STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); +STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep); static void help(void) { printf("%s [OPTIONS...] IMAGE\n" "%s [OPTIONS...] --mount IMAGE PATH\n" "Dissect a file system OS image.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -m --mount Mount the image to the specified directory\n" - " -r --read-only Mount read-only\n" - " --fsck=BOOL Run fsck before mounting\n" - " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n" - " --root-hash=HASH Specify root hash for verity\n", + " -h --help Show this help\n" + " --version Show package version\n" + " -m --mount Mount the image to the specified directory\n" + " -r --read-only Mount read-only\n" + " --fsck=BOOL Run fsck before mounting\n" + " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n" + " --root-hash=HASH Specify root hash for verity\n" + " --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n" + " as a DER encoded PKCS7, either as a path to a file\n" + " or as an ASCII base64 encoded string prefixed by\n" + " 'base64:'\n" + " --verity-data=PATH Specify data file with hash tree for verity if it is\n" + " not embedded in IMAGE\n", program_invocation_short_name, program_invocation_short_name); } @@ -51,16 +65,20 @@ static int parse_argv(int argc, char *argv[]) { ARG_DISCARD, ARG_ROOT_HASH, ARG_FSCK, + ARG_VERITY_DATA, + ARG_ROOT_HASH_SIG, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "mount", no_argument, NULL, 'm' }, - { "read-only", no_argument, NULL, 'r' }, - { "discard", required_argument, NULL, ARG_DISCARD }, - { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, - { "fsck", required_argument, NULL, ARG_FSCK }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "mount", no_argument, NULL, 'm' }, + { "read-only", no_argument, NULL, 'r' }, + { "discard", required_argument, NULL, ARG_DISCARD }, + { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, + { "fsck", required_argument, NULL, ARG_FSCK }, + { "verity-data", required_argument, NULL, ARG_VERITY_DATA }, + { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG }, {} }; @@ -127,6 +145,37 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_VERITY_DATA: + r = parse_path_argument_and_warn(optarg, false, &arg_verity_data); + if (r < 0) + return r; + break; + + case ARG_ROOT_HASH_SIG: { + char *value; + + if ((value = startswith(optarg, "base64:"))) { + void *p; + size_t l; + + r = unbase64mem(value, strlen(value), &p, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg); + + free_and_replace(arg_root_hash_sig, p); + arg_root_hash_sig_size = l; + arg_root_hash_sig_path = mfree(arg_root_hash_sig_path); + } else { + r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path); + if (r < 0) + return r; + arg_root_hash_sig = mfree(arg_root_hash_sig); + arg_root_hash_sig_size = 0; + } + + break; + } + case ARG_FSCK: r = parse_boolean(optarg); if (r < 0) @@ -188,13 +237,14 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to set up loopback device: %m"); - if (!arg_root_hash) { - r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size); - if (r < 0) - return log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image); - } + r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size, + arg_verity_data ? NULL : &arg_verity_data, + arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path); + if (r < 0) + return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image); + arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; - r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_flags, &m); + r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &m); if (r < 0) return r; @@ -205,7 +255,6 @@ static int run(int argc, char *argv[]) { for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { DissectedPartition *p = m->partitions + i; - int k; if (!p->found) continue; @@ -223,9 +272,8 @@ static int run(int argc, char *argv[]) { if (p->architecture != _ARCHITECTURE_INVALID) printf(" for %s", architecture_to_string(p->architecture)); - k = PARTITION_VERITY_OF(i); - if (k >= 0) - printf(" %s verity", m->partitions[k].found ? "with" : "without"); + if (dissected_image_can_do_verity(m, i)) + printf(" %s verity", dissected_image_has_verity(m, i) ? "with" : "without"); if (p->partno >= 0) printf(" on partition #%i", p->partno); @@ -268,7 +316,7 @@ static int run(int argc, char *argv[]) { } case ACTION_MOUNT: - r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di); + r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, arg_flags, &di); if (r < 0) return r; diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 5fe51359f..4a14c23f1 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -20,7 +20,7 @@ static int environment_dirs(char ***ret) { return -ENOMEM; /* ~/.config/systemd/environment.d */ - r = sd_path_home(SD_PATH_USER_CONFIGURATION, "environment.d", &c); + r = sd_path_lookup(SD_PATH_USER_CONFIGURATION, "environment.d", &c); if (r < 0) return r; diff --git a/src/escape/escape.c b/src/escape/escape.c index 9066c3085..0c543a90f 100644 --- a/src/escape/escape.c +++ b/src/escape/escape.c @@ -159,9 +159,7 @@ static int run(int argc, char *argv[]) { char **i; int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 901fbf081..a3f442518 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -2,6 +2,7 @@ #include #include +#include #include #include "sd-id128.h" @@ -9,6 +10,7 @@ #include "alloc-util.h" #include "ask-password-api.h" #include "copy.h" +#include "dissect-image.h" #include "env-file.h" #include "fd-util.h" #include "fileio.h" @@ -17,9 +19,12 @@ #include "kbd-util.h" #include "libcrypt-util.h" #include "locale-util.h" +#include "loop-util.h" #include "main-func.h" #include "memory-util.h" #include "mkdir.h" +#include "mount-util.h" +#include "namespace-util.h" #include "os-util.h" #include "parse-util.h" #include "path-util.h" @@ -30,10 +35,13 @@ #include "strv.h" #include "terminal-util.h" #include "time-util.h" +#include "tmpfile-util-label.h" +#include "tmpfile-util.h" #include "umask-util.h" #include "user-util.h" static char *arg_root = NULL; +static char *arg_image = NULL; static char *arg_locale = NULL; /* $LANG */ static char *arg_keymap = NULL; static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ @@ -41,17 +49,26 @@ static char *arg_timezone = NULL; static char *arg_hostname = NULL; static sd_id128_t arg_machine_id = {}; static char *arg_root_password = NULL; +static char *arg_root_shell = NULL; +static char *arg_kernel_cmdline = NULL; static bool arg_prompt_locale = false; static bool arg_prompt_keymap = false; static bool arg_prompt_timezone = false; static bool arg_prompt_hostname = false; static bool arg_prompt_root_password = false; +static bool arg_prompt_root_shell = false; static bool arg_copy_locale = false; static bool arg_copy_keymap = false; static bool arg_copy_timezone = false; static bool arg_copy_root_password = false; +static bool arg_copy_root_shell = false; +static bool arg_force = false; +static bool arg_delete_root_password = false; +static bool arg_root_password_is_hashed = false; +static bool arg_welcome = true; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); +STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_locale, freep); STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep); STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep); @@ -80,6 +97,9 @@ static void print_welcome(void) { const char *pn; int r; + if (!arg_welcome) + return; + if (done) return; @@ -205,6 +225,14 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i } } +static bool locale_is_ok(const char *name) { + + if (arg_root) + return locale_is_valid(name); + + return locale_is_installed(name) > 0; +} + static int prompt_locale(void) { _cleanup_strv_free_ char **locales = NULL; int r; @@ -238,7 +266,7 @@ static int prompt_locale(void) { print_welcome(); r = prompt_loop("Please enter system locale name or number", - locales, 60, locale_is_valid, &arg_locale); + locales, 60, locale_is_ok, &arg_locale); if (r < 0) return r; @@ -246,7 +274,7 @@ static int prompt_locale(void) { return 0; r = prompt_loop("Please enter system message locale name or number", - locales, 60, locale_is_valid, &arg_locale_messages); + locales, 60, locale_is_ok, &arg_locale_messages); if (r < 0) return r; @@ -265,7 +293,7 @@ static int process_locale(void) { int r; etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); - if (laccess(etc_localeconf, F_OK) >= 0) + if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_locale && arg_root) { @@ -332,7 +360,7 @@ static int process_keymap(void) { int r; etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf"); - if (laccess(etc_vconsoleconf, F_OK) >= 0) + if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_keymap && arg_root) { @@ -404,7 +432,7 @@ static int process_timezone(void) { int r; etc_localtime = prefix_roota(arg_root, "/etc/localtime"); - if (laccess(etc_localtime, F_OK) >= 0) + if (laccess(etc_localtime, F_OK) >= 0 && !arg_force) return 0; if (arg_copy_timezone && arg_root) { @@ -484,7 +512,7 @@ static int process_hostname(void) { int r; etc_hostname = prefix_roota(arg_root, "/etc/hostname"); - if (laccess(etc_hostname, F_OK) >= 0) + if (laccess(etc_hostname, F_OK) >= 0 && !arg_force) return 0; r = prompt_hostname(); @@ -495,7 +523,8 @@ static int process_hostname(void) { return 0; r = write_string_file(etc_hostname, arg_hostname, - WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755); + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_hostname); @@ -509,14 +538,15 @@ static int process_machine_id(void) { int r; etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - if (laccess(etc_machine_id, F_OK) >= 0) + if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force) return 0; if (sd_id128_is_null(arg_machine_id)) return 0; r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), - WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755); + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); if (r < 0) return log_error_errno(r, "Failed to write machine id: %m"); @@ -525,7 +555,7 @@ static int process_machine_id(void) { } static int prompt_root_password(void) { - const char *msg1, *msg2, *etc_shadow; + const char *msg1, *msg2; int r; if (arg_root_password) @@ -534,10 +564,6 @@ static int prompt_root_password(void) { if (!arg_prompt_root_password) return 0; - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - print_welcome(); putchar('\n'); @@ -578,95 +604,281 @@ static int prompt_root_password(void) { return 0; } -static int write_root_shadow(const char *path, const struct spwd *p) { - _cleanup_fclose_ FILE *f = NULL; +static int find_shell(const char *path, const char *root) { int r; assert(path); - assert(p); - RUN_WITH_UMASK(0777) - f = fopen(path, "wex"); - if (!f) - return -errno; + if (!valid_shell(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path); - r = putspent_sane(p, f); + r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, NULL, NULL); + if (r < 0) { + const char *p; + p = prefix_roota(root, path); + return log_error_errno(r, "Failed to resolve shell %s: %m", p); + } + + return 0; +} + +static int prompt_root_shell(void) { + int r; + + if (arg_root_shell || !arg_prompt_root_shell) + return 0; + + print_welcome(); + putchar('\n'); + + for (;;) { + _cleanup_free_ char *s = NULL; + + r = ask_string(&s, "%s Please enter root shell for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET)); + if (r < 0) + return log_error_errno(r, "Failed to query root shell: %m"); + + if (isempty(s)) { + log_warning("No shell entered, skipping."); + break; + } + + r = find_shell(s, arg_root); + if (r < 0) + continue; + + arg_root_shell = TAKE_PTR(s); + break; + } + + return 0; +} + +static int write_root_passwd(const char *passwd_path, const char *password, const char *shell) { + _cleanup_fclose_ FILE *original = NULL, *passwd = NULL; + _cleanup_(unlink_and_freep) char *passwd_tmp = NULL; + int r; + + assert(password); + + r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); if (r < 0) return r; - return fflush_sync_and_check(f); + original = fopen(passwd_path, "re"); + if (original) { + struct passwd *i; + + r = sync_rights(fileno(original), fileno(passwd)); + if (r < 0) + return r; + + while ((r = fgetpwent_sane(original, &i)) > 0) { + + if (streq(i->pw_name, "root")) { + i->pw_passwd = (char *) password; + if (shell) + i->pw_shell = (char *) shell; + } + + r = putpwent_sane(i, passwd); + if (r < 0) + return r; + } + if (r < 0) + return r; + + } else { + struct passwd root = { + .pw_name = (char *) "root", + .pw_passwd = (char *) password, + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = (char *) "Super User", + .pw_dir = (char *) "/root", + .pw_shell = (char *) (shell ?: "/bin/sh"), + }; + + if (errno != ENOENT) + return -errno; + + r = fchmod(fileno(passwd), 0644); + if (r < 0) + return -errno; + + r = putpwent_sane(&root, passwd); + if (r < 0) + return r; + } + + r = fflush_sync_and_check(passwd); + if (r < 0) + return r; + + r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path); + if (r < 0) + return r; + + return 0; } -static int process_root_password(void) { - - struct spwd item = { - .sp_namp = (char*) "root", - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; - _cleanup_free_ char *salt = NULL; - _cleanup_close_ int lock = -1; - struct crypt_data cd = {}; - - const char *etc_shadow; +static int write_root_shadow(const char *shadow_path, const char *hashed_password) { + _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; + _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; int r; + assert(hashed_password); + + r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + if (r < 0) + return r; + + original = fopen(shadow_path, "re"); + if (original) { + struct spwd *i; + + r = sync_rights(fileno(original), fileno(shadow)); + if (r < 0) + return r; + + while ((r = fgetspent_sane(original, &i)) > 0) { + + if (streq(i->sp_namp, "root")) { + i->sp_pwdp = (char *) hashed_password; + i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + } + + r = putspent_sane(i, shadow); + if (r < 0) + return r; + } + if (r < 0) + return r; + + } else { + struct spwd root = { + .sp_namp = (char*) "root", + .sp_pwdp = (char *) hashed_password, + .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY), + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + if (errno != ENOENT) + return -errno; + + r = fchmod(fileno(shadow), 0000); + if (r < 0) + return -errno; + + r = putspent_sane(&root, shadow); + if (r < 0) + return r; + } + + r = fflush_sync_and_check(shadow); + if (r < 0) + return r; + + r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path); + if (r < 0) + return r; + + return 0; +} + +static int process_root_args(void) { + _cleanup_close_ int lock = -1; + struct crypt_data cd = {}; + const char *password, *hashed_password; + const char *etc_passwd, *etc_shadow; + int r; + + etc_passwd = prefix_roota(arg_root, "/etc/passwd"); etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) + + /* We only mess with passwd and shadow if both do not exist or --force is specified. These files are + * tightly coupled and hence we make sure we have permission from the user to create/modify both + * files. */ + if ((laccess(etc_passwd, F_OK) >= 0 || laccess(etc_shadow, F_OK) >= 0) && !arg_force) return 0; - (void) mkdir_parents(etc_shadow, 0755); + (void) mkdir_parents(etc_passwd, 0755); lock = take_etc_passwd_lock(arg_root); if (lock < 0) - return log_error_errno(lock, "Failed to take a lock: %m"); + return log_error_errno(lock, "Failed to take a lock on %s: %m", etc_passwd); + + if (arg_copy_root_shell && arg_root) { + struct passwd *p; + + errno = 0; + p = getpwnam("root"); + if (!p) + return log_error_errno(errno_or_else(EIO), "Failed to find passwd entry for root: %m"); + + r = free_and_strdup(&arg_root_shell, p->pw_shell); + if (r < 0) + return log_oom(); + } + + r = prompt_root_shell(); + if (r < 0) + return r; if (arg_copy_root_password && arg_root) { struct spwd *p; errno = 0; p = getspnam("root"); - if (p || errno != ENOENT) { - if (!p) { - if (!errno) - errno = EIO; + if (!p) + return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m"); - return log_error_errno(errno, "Failed to find shadow entry for root: %m"); - } + r = free_and_strdup(&arg_root_password, p->sp_pwdp); + if (r < 0) + return log_oom(); - r = write_root_shadow(etc_shadow, p); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_shadow); - - log_info("%s copied.", etc_shadow); - return 0; - } + arg_root_password_is_hashed = true; } r = prompt_root_password(); if (r < 0) return r; - if (!arg_root_password) - return 0; + if (arg_root_password && arg_root_password_is_hashed) { + password = "x"; + hashed_password = arg_root_password; + } else if (arg_root_password) { + _cleanup_free_ char *salt = NULL; + /* hashed_password points inside cd after crypt_r returns so cd has function scope. */ - r = make_salt(&salt); + password = "x"; + + r = make_salt(&salt); + if (r < 0) + return log_error_errno(r, "Failed to get salt: %m"); + + errno = 0; + hashed_password = crypt_r(arg_root_password, salt, &cd); + if (!hashed_password) + return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno, + "Failed to encrypt password: %m"); + } else if (arg_delete_root_password) + password = hashed_password = ""; + else + password = hashed_password = "!"; + + r = write_root_passwd(etc_passwd, password, arg_root_shell); if (r < 0) - return log_error_errno(r, "Failed to get salt: %m"); + return log_error_errno(r, "Failed to write %s: %m", etc_passwd); - errno = 0; - item.sp_pwdp = crypt_r(arg_root_password, salt, &cd); - if (!item.sp_pwdp) - return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno, - "Failed to encrypt password: %m"); + log_info("%s written", etc_passwd); - item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - r = write_root_shadow(etc_shadow, &item); + r = write_root_shadow(etc_shadow, hashed_password); if (r < 0) return log_error_errno(r, "Failed to write %s: %m", etc_shadow); @@ -674,6 +886,96 @@ static int process_root_password(void) { return 0; } +static int process_kernel_cmdline(void) { + const char *etc_kernel_cmdline; + int r; + + etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline"); + if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force) + return 0; + + if (!arg_kernel_cmdline) + return 0; + + r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline, + WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 | + (arg_force ? WRITE_STRING_FILE_ATOMIC : 0)); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_kernel_cmdline); + + log_info("%s written.", etc_kernel_cmdline); + return 0; +} + +static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image) { + DissectImageFlags f = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK; + _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; + _cleanup_(rmdir_and_freep) char *mount_dir = NULL; + _cleanup_free_ char *temp = NULL; + int r; + + if (!arg_image) { + *ret_mount_dir = NULL; + *ret_decrypted_image = NULL; + *ret_loop_device = NULL; + return 0; + } + + assert(!arg_root); + + r = tempfn_random_child(NULL, "firstboot", &temp); + if (r < 0) + return log_error_errno(r, "Failed to generate temporary mount directory: %m"); + + r = loop_device_make_by_path(arg_image, O_RDWR, LO_FLAGS_PARTSCAN, &d); + if (r < 0) + return log_error_errno(r, "Failed to set up loopback device: %m"); + + r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, f, &dissected_image); + if (r < 0) + return r; + + r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, f, &decrypted_image); + if (r < 0) + return r; + + r = detach_mount_namespace(); + if (r < 0) + return log_error_errno(r, "Failed to detach mount namespace: %m"); + + mount_dir = strdup(temp); + if (!mount_dir) + return log_oom(); + + r = mkdir_p(mount_dir, 0700); + if (r < 0) { + mount_dir = mfree(mount_dir); + return log_error_errno(r, "Failed to create mount point: %m"); + } + + r = dissected_image_mount(dissected_image, mount_dir, UID_INVALID, f); + if (r < 0) + return log_error_errno(r, "Failed to mount image: %m"); + + if (decrypted_image) { + r = decrypted_image_relinquish(decrypted_image); + if (r < 0) + return log_error_errno(r, "Failed to relinquish DM devices: %m"); + } + + loop_device_relinquish(d); + + arg_root = TAKE_PTR(temp); + + *ret_mount_dir = TAKE_PTR(mount_dir); + *ret_decrypted_image = TAKE_PTR(decrypted_image); + *ret_loop_device = TAKE_PTR(d); + + return 1; +} + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -684,29 +986,37 @@ static int help(void) { printf("%s [OPTIONS...]\n\n" "Configures basic settings of the system.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --locale=LOCALE Set primary locale (LANG=)\n" - " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" - " --keymap=KEYMAP Set keymap\n" - " --timezone=TIMEZONE Set timezone\n" - " --hostname=NAME Set host name\n" - " --machine-ID=ID Set machine ID\n" - " --root-password=PASSWORD Set root password\n" - " --root-password-file=FILE Set root password from file\n" - " --prompt-locale Prompt the user for locale settings\n" - " --prompt-keymap Prompt the user for keymap settings\n" - " --prompt-timezone Prompt the user for timezone\n" - " --prompt-hostname Prompt the user for hostname\n" - " --prompt-root-password Prompt the user for root password\n" - " --prompt Prompt for all of the above\n" - " --copy-locale Copy locale from host\n" - " --copy-keymap Copy keymap from host\n" - " --copy-timezone Copy timezone from host\n" - " --copy-root-password Copy root password from host\n" - " --copy Copy locale, keymap, timezone, root password\n" - " --setup-machine-id Generate a new random machine ID\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + " --image=PATH Operate on an alternate filesystem image\n" + " --locale=LOCALE Set primary locale (LANG=)\n" + " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" + " --keymap=KEYMAP Set keymap\n" + " --timezone=TIMEZONE Set timezone\n" + " --hostname=NAME Set hostname\n" + " --machine-ID=ID Set machine ID\n" + " --root-password=PASSWORD Set root password from plaintext password\n" + " --root-password-file=FILE Set root password from file\n" + " --root-password-hashed=HASHED_PASSWORD Set root password from hashed password\n" + " --root-shell=SHELL Set root shell\n" + " --prompt-locale Prompt the user for locale settings\n" + " --prompt-keymap Prompt the user for keymap settings\n" + " --prompt-timezone Prompt the user for timezone\n" + " --prompt-hostname Prompt the user for hostname\n" + " --prompt-root-password Prompt the user for root password\n" + " --prompt-root-shell Prompt the user for root shell\n" + " --prompt Prompt for all of the above\n" + " --copy-locale Copy locale from host\n" + " --copy-keymap Copy keymap from host\n" + " --copy-timezone Copy timezone from host\n" + " --copy-root-password Copy root password from host\n" + " --copy-root-shell Copy root shell from host\n" + " --copy Copy locale, keymap, timezone, root password\n" + " --setup-machine-id Generate a new random machine ID\n" + " --force Overwrite existing files\n" + " --delete-root-password Delete root password\n" + " --welcome=no Disable the welcome text\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -720,6 +1030,7 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_ROOT, + ARG_IMAGE, ARG_LOCALE, ARG_LOCALE_MESSAGES, ARG_KEYMAP, @@ -728,44 +1039,61 @@ static int parse_argv(int argc, char *argv[]) { ARG_MACHINE_ID, ARG_ROOT_PASSWORD, ARG_ROOT_PASSWORD_FILE, + ARG_ROOT_PASSWORD_HASHED, + ARG_ROOT_SHELL, + ARG_KERNEL_COMMAND_LINE, ARG_PROMPT, ARG_PROMPT_LOCALE, ARG_PROMPT_KEYMAP, ARG_PROMPT_TIMEZONE, ARG_PROMPT_HOSTNAME, ARG_PROMPT_ROOT_PASSWORD, + ARG_PROMPT_ROOT_SHELL, ARG_COPY, ARG_COPY_LOCALE, ARG_COPY_KEYMAP, ARG_COPY_TIMEZONE, ARG_COPY_ROOT_PASSWORD, + ARG_COPY_ROOT_SHELL, ARG_SETUP_MACHINE_ID, + ARG_FORCE, + ARG_DELETE_ROOT_PASSWORD, + ARG_WELCOME, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "locale", required_argument, NULL, ARG_LOCALE }, - { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, - { "keymap", required_argument, NULL, ARG_KEYMAP }, - { "timezone", required_argument, NULL, ARG_TIMEZONE }, - { "hostname", required_argument, NULL, ARG_HOSTNAME }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, - { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, - { "prompt", no_argument, NULL, ARG_PROMPT }, - { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, - { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, - { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, - { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, - { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, - { "copy", no_argument, NULL, ARG_COPY }, - { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, - { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, - { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, - { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, - { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + { "image", required_argument, NULL, ARG_IMAGE }, + { "locale", required_argument, NULL, ARG_LOCALE }, + { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, + { "keymap", required_argument, NULL, ARG_KEYMAP }, + { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, + { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, + { "root-password-hashed", required_argument, NULL, ARG_ROOT_PASSWORD_HASHED }, + { "root-shell", required_argument, NULL, ARG_ROOT_SHELL }, + { "kernel-command-line", required_argument, NULL, ARG_KERNEL_COMMAND_LINE }, + { "prompt", no_argument, NULL, ARG_PROMPT }, + { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, + { "prompt-keymap", no_argument, NULL, ARG_PROMPT_KEYMAP }, + { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, + { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, + { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, + { "prompt-root-shell", no_argument, NULL, ARG_PROMPT_ROOT_SHELL }, + { "copy", no_argument, NULL, ARG_COPY }, + { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, + { "copy-keymap", no_argument, NULL, ARG_COPY_KEYMAP }, + { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, + { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, + { "copy-root-shell", no_argument, NULL, ARG_COPY_ROOT_SHELL }, + { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + { "force", no_argument, NULL, ARG_FORCE }, + { "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD }, + { "welcome", required_argument, NULL, ARG_WELCOME }, {} }; @@ -790,11 +1118,13 @@ static int parse_argv(int argc, char *argv[]) { return r; break; - case ARG_LOCALE: - if (!locale_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Locale %s is not valid.", optarg); + case ARG_IMAGE: + r = parse_path_argument_and_warn(optarg, false, &arg_image); + if (r < 0) + return r; + break; + case ARG_LOCALE: r = free_and_strdup(&arg_locale, optarg); if (r < 0) return log_oom(); @@ -802,10 +1132,6 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_LOCALE_MESSAGES: - if (!locale_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Locale %s is not valid.", optarg); - r = free_and_strdup(&arg_locale_messages, optarg); if (r < 0) return log_oom(); @@ -838,6 +1164,8 @@ static int parse_argv(int argc, char *argv[]) { r = free_and_strdup(&arg_root_password, optarg); if (r < 0) return log_oom(); + + arg_root_password_is_hashed = false; break; case ARG_ROOT_PASSWORD_FILE: @@ -847,6 +1175,26 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to read %s: %m", optarg); + arg_root_password_is_hashed = false; + break; + + case ARG_ROOT_PASSWORD_HASHED: + r = free_and_strdup(&arg_root_password, optarg); + if (r < 0) + return log_oom(); + + arg_root_password_is_hashed = true; + break; + + case ARG_ROOT_SHELL: + r = find_shell(optarg, arg_root); + if (r < 0) + return r; + + r = free_and_strdup(&arg_root_shell, optarg); + if (r < 0) + return log_oom(); + break; case ARG_HOSTNAME: @@ -868,8 +1216,16 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_KERNEL_COMMAND_LINE: + r = free_and_strdup(&arg_kernel_cmdline, optarg); + if (r < 0) + return log_oom(); + + break; + case ARG_PROMPT: - arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; + arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = + arg_prompt_root_password = arg_prompt_root_shell = true; break; case ARG_PROMPT_LOCALE: @@ -892,8 +1248,13 @@ static int parse_argv(int argc, char *argv[]) { arg_prompt_root_password = true; break; + case ARG_PROMPT_ROOT_SHELL: + arg_prompt_root_shell = true; + break; + case ARG_COPY: - arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = true; + arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = + arg_copy_root_shell = true; break; case ARG_COPY_LOCALE: @@ -912,14 +1273,33 @@ static int parse_argv(int argc, char *argv[]) { arg_copy_root_password = true; break; - case ARG_SETUP_MACHINE_ID: + case ARG_COPY_ROOT_SHELL: + arg_copy_root_shell = true; + break; + case ARG_SETUP_MACHINE_ID: r = sd_id128_randomize(&arg_machine_id); if (r < 0) return log_error_errno(r, "Failed to generate randomized machine ID: %m"); break; + case ARG_FORCE: + arg_force = true; + break; + + case ARG_DELETE_ROOT_PASSWORD: + arg_delete_root_password = true; + break; + + case ARG_WELCOME: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --welcome= argument: %s", optarg); + + arg_welcome = r; + break; + case '?': return -EINVAL; @@ -927,11 +1307,28 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + /* We check if the specified locale strings are valid down here, so that we can take --root= into + * account when looking for the locale files. */ + + if (arg_locale && !locale_is_ok(arg_locale)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale); + if (arg_locale_messages && !locale_is_ok(arg_locale_messages)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages); + + if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--delete-root-password cannot be combined with other root password options"); + + 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."); + return 1; } static int run(int argc, char *argv[]) { - bool enabled; + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL; int r; r = parse_argv(argc, argv); @@ -942,11 +1339,23 @@ static int run(int argc, char *argv[]) { umask(0022); - r = proc_cmdline_get_bool("systemd.firstboot", &enabled); + if (!arg_root && !arg_image) { + bool enabled; + + /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel + * command line option, because we are called to provision the host with basic settings (as + * opposed to some other file system tree/image) */ + + r = proc_cmdline_get_bool("systemd.firstboot", &enabled); + if (r < 0) + return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); + if (r > 0 && !enabled) + return 0; /* disabled */ + } + + r = setup_image(&unlink_dir, &loop_device, &decrypted_image); if (r < 0) - return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); - if (r > 0 && !enabled) - return 0; /* disabled */ + return r; r = process_locale(); if (r < 0) @@ -968,7 +1377,11 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - r = process_root_password(); + r = process_root_args(); + if (r < 0) + return r; + + r = process_kernel_cmdline(); if (r < 0) return r; diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 7cdfbc0a1..91581aed3 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -35,11 +35,13 @@ typedef enum MountpointFlags { AUTOMOUNT = 1 << 2, MAKEFS = 1 << 3, GROWFS = 1 << 4, + RWONLY = 1 << 5, } MountpointFlags; static const char *arg_dest = NULL; static const char *arg_dest_late = NULL; static bool arg_fstab_enabled = true; +static bool arg_swap_enabled = true; static char *arg_root_what = NULL; static char *arg_root_fstype = NULL; static char *arg_root_options = NULL; @@ -98,6 +100,11 @@ static int add_swap( assert(what); assert(me); + if (!arg_swap_enabled) { + log_info("Swap unit generation disabled on kernel command line, ignoring fstab swap entry for %s.", what); + return 0; + } + if (access("/proc/swaps", F_OK) < 0) { log_info("Swap not supported, ignoring fstab swap entry for %s.", what); return 0; @@ -256,10 +263,10 @@ static int write_dependency( res = strv_join(units, " "); if (!res) return log_oom(); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" + + DISABLE_WARNING_FORMAT_NONLITERAL; fprintf(f, format, res); -#pragma GCC diagnostic pop + REENABLE_WARNING; } return 0; @@ -307,6 +314,29 @@ static int write_requires_mounts_for(FILE *f, const char *opts) { return 0; } +static int write_extra_dependencies(FILE *f, const char *opts) { + int r; + + assert(f); + + if (opts) { + r = write_after(f, opts); + if (r < 0) + return r; + r = write_requires_after(f, opts); + if (r < 0) + return r; + r = write_before(f, opts); + if (r < 0) + return r; + r = write_requires_mounts_for(f, opts); + if (r < 0) + return r; + } + + return 0; +} + static int add_mount( const char *dest, const char *what, @@ -399,20 +429,9 @@ static int add_mount( SET_FLAG(flags, NOFAIL, true); } - if (opts) { - r = write_after(f, opts); - if (r < 0) - return r; - r = write_requires_after(f, opts); - if (r < 0) - return r; - r = write_before(f, opts); - if (r < 0) - return r; - r = write_requires_mounts_for(f, opts); - if (r < 0) - return r; - } + r = write_extra_dependencies(f, opts); + if (r < 0) + return r; if (passno != 0) { r = generator_write_fsck_deps(f, dest, what, where, fstype); @@ -466,6 +485,9 @@ static int add_mount( if (r < 0) return r; + if (flags & RWONLY) + fprintf(f, "ReadWriteOnly=yes\n"); + r = fflush_and_check(f); if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", name); @@ -562,7 +584,7 @@ static int parse_fstab(bool initrd) { while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL; - bool makefs, growfs, noauto, nofail; + bool makefs, growfs, noauto, nofail, rwonly; int k; if (initrd && !mount_in_initrd(me)) @@ -602,6 +624,7 @@ static int parse_fstab(bool initrd) { makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0"); growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0"); + rwonly = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0"); noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); @@ -634,7 +657,7 @@ static int parse_fstab(bool initrd) { me->mnt_type, me->mnt_opts, me->mnt_passno, - makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, + makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT | rwonly*RWONLY, post, fstab); } @@ -777,7 +800,7 @@ static int add_volatile_var(void) { "/var", NULL, "tmpfs", - "mode=0755", + "mode=0755" TMPFS_LIMITS_VAR, 0, 0, SPECIAL_LOCAL_FS_TARGET, @@ -870,6 +893,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat arg_volatile_mode = m; } else arg_volatile_mode = VOLATILE_YES; + + } else if (streq(key, "systemd.swap")) { + + r = value ? parse_boolean(value) : 1; + if (r < 0) + log_warning("Failed to parse systemd.swap switch %s. Ignoring.", value); + else + arg_swap_enabled = r; } return 0; diff --git a/src/fuzz/fuzz-udev-rules.c b/src/fuzz/fuzz-udev-rules.c index 3194f7aa8..7530d80be 100644 --- a/src/fuzz/fuzz-udev-rules.c +++ b/src/fuzz/fuzz-udev-rules.c @@ -1,106 +1,36 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include -#include -#include -#include +#include #include "fd-util.h" #include "fs-util.h" #include "fuzz.h" -#include "log.h" -#include "mkdir.h" -#include "rm-rf.h" -#include "string-util.h" #include "tests.h" +#include "tmpfile-util.h" #include "udev-rules.h" -static struct fakefs { - const char *target; - bool ignore_mount_error; - bool is_mounted; -} fakefss[] = { - { "/sys", false, false }, - { "/dev", false, false }, - { "/run", false, false }, - { "/etc", false, false }, - { UDEVLIBEXECDIR "/rules.d", true, false }, -}; - -static int setup_mount_namespace(void) { - static thread_local bool is_namespaced = false; - - if (is_namespaced) - return 1; - - if (unshare(CLONE_NEWNS) < 0) - return log_error_errno(errno, "Failed to call unshare(): %m"); - - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - return log_error_errno(errno, "Failed to mount / as private: %m"); - - is_namespaced = true; - - return 1; -} - -static int setup_fake_filesystems(const char *runtime_dir) { - for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) { - if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { - log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target); - if (!fakefss[i].ignore_mount_error) - return -errno; - } else - fakefss[i].is_mounted = true; - } - - return 0; -} - -static int cleanup_fake_filesystems(const char *runtime_dir) { - for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) { - if (!fakefss[i].is_mounted) - continue; - - if (umount(fakefss[i].target) < 0) { - log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target); - if (!fakefss[i].ignore_mount_error) - return -errno; - } else - fakefss[i].is_mounted = false; - } - return 0; -} - int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(udev_rules_freep) UdevRules *rules = NULL; - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - FILE *f = NULL; - - (void) setup_mount_namespace(); - - assert_se(runtime_dir = setup_fake_runtime_dir()); - - if (setup_fake_filesystems(runtime_dir) < 0) { -#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - return EXIT_TEST_SKIP; -#endif - } + _cleanup_fclose_ FILE *f = NULL; + _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-udev-rules.XXXXXX"; + int r; if (!getenv("SYSTEMD_LOG_LEVEL")) { log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT); log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT); } - assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0); - f = fopen("/etc/udev/rules.d/fuzz.rules", "we"); - assert_se(f); + assert_se(fmkostemp_safe(filename, "r+", &f) == 0); if (size != 0) assert_se(fwrite(data, size, 1, f) == 1); - assert_se(fclose(f) == 0); + fflush(f); - assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0); + assert_se(rules = udev_rules_new(RESOLVE_NAME_EARLY)); + r = udev_rules_parse_file(rules, filename); + log_info_errno(r, "Parsing %s: %m", filename); + assert_se(IN_SET(r, + 0, /* OK */ + -ENOBUFS /* line length exceeded */)); - assert_se(cleanup_fake_filesystems(runtime_dir) >= 0); return 0; } diff --git a/src/fuzz/fuzz-unit-file.c b/src/fuzz/fuzz-unit-file.c index d3993cf12..c0661433a 100644 --- a/src/fuzz/fuzz-unit-file.c +++ b/src/fuzz/fuzz-unit-file.c @@ -70,10 +70,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { name = strjoina("a.", unit_type_to_string(t)); assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0); - (void) config_parse(name, name, f, - UNIT_VTABLE(u)->sections, - config_item_perf_lookup, load_fragment_gperf_lookup, - CONFIG_PARSE_ALLOW_INCLUDE, u); + (void) config_parse( + name, name, f, + UNIT_VTABLE(u)->sections, + config_item_perf_lookup, load_fragment_gperf_lookup, + 0, + u, + NULL); g = open_memstream_unlocked(&out, &out_size); assert_se(g); diff --git a/src/fuzz/fuzz-xdg-desktop.c b/src/fuzz/fuzz-xdg-desktop.c new file mode 100644 index 000000000..f8a1b5b28 --- /dev/null +++ b/src/fuzz/fuzz-xdg-desktop.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "rm-rf.h" +#include "string-util.h" +#include "strv.h" +#include "tests.h" +#include "tmpfile-util.h" +#include "fuzz.h" +#include "xdg-autostart-service.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-xdg-desktop.XXXXXX"; + _cleanup_close_ int fd = -1; + _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; + + /* We don't want to fill the logs with messages about parse errors. + * Disable most logging if not running standalone */ + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + assert_se(mkdtemp_malloc("/tmp/fuzz-xdg-desktop-XXXXXX", &tmpdir) >= 0); + + fd = mkostemp_safe(name); + assert_se(fd >= 0); + assert_se(write(fd, data, size) == (ssize_t) size); + + assert_se(service = xdg_autostart_service_parse_desktop(name)); + assert_se(service->name = strdup("fuzz-xdg-desktop.service")); + (void) xdg_autostart_service_generate_unit(service, tmpdir); + + return 0; +} diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h index 83b1ac11a..1e5652625 100644 --- a/src/fuzz/fuzz.h +++ b/src/fuzz/fuzz.h @@ -6,6 +6,3 @@ /* The entry point into the fuzzer */ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); - -/* https://docs.fuzzbuzz.io/developer-documentation/porting-targets-to-fuzzbuzz/libfuzzer-targets */ -int FuzzerEntrypoint(const uint8_t *data, size_t size); diff --git a/src/fuzz/fuzzer-entry-point.c b/src/fuzz/fuzzer-entry-point.c deleted file mode 100644 index 020c11165..000000000 --- a/src/fuzz/fuzzer-entry-point.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "fuzz.h" - -int FuzzerEntrypoint(const uint8_t *data, size_t size) { - return LLVMFuzzerTestOneInput(data, size); -} diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build index c88812d1d..01f119fcd 100644 --- a/src/fuzz/meson.build +++ b/src/fuzz/meson.build @@ -146,4 +146,10 @@ fuzzers += [ [['src/fuzz/fuzz-time-util.c'], [libshared], []], + + [['src/fuzz/fuzz-xdg-desktop.c', + 'src/xdg-autostart-generator/xdg-autostart-service.h', + 'src/xdg-autostart-generator/xdg-autostart-service.c'], + [], + []], ] diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 2067eeaf8..a9478b9db 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) { if (r <= 0) return r; - r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m); + r = dissect_image(fd, NULL, 0, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m); if (r == -ENOPKG) { log_debug_errno(r, "No suitable partition table found, ignoring."); return 0; diff --git a/src/home/home-util.c b/src/home/home-util.c index 69ab64548..3fd57639f 100644 --- a/src/home/home-util.c +++ b/src/home/home-util.c @@ -64,6 +64,12 @@ int suitable_image_path(const char *path) { path_is_absolute(path); } +bool supported_fstype(const char *fstype) { + /* Limit the set of supported file systems a bit, as protection against little tested kernel file + * systems. Also, we only support the resize ioctls for these file systems. */ + return STR_IN_SET(fstype, "ext4", "btrfs", "xfs"); +} + int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) { _cleanup_free_ char *user_name = NULL, *realm = NULL; const char *c; @@ -124,6 +130,8 @@ int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) { if (r < 0) return r; + (void) sd_bus_message_sensitive(m); + return sd_bus_message_append(m, "s", formatted); } diff --git a/src/home/home-util.h b/src/home/home-util.h index df20c0af7..6161d4c3d 100644 --- a/src/home/home-util.h +++ b/src/home/home-util.h @@ -12,12 +12,14 @@ bool suitable_user_name(const char *name); int suitable_realm(const char *realm); int suitable_image_path(const char *path); +bool supported_fstype(const char *fstype); + int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm); int bus_message_append_secret(sd_bus_message *m, UserRecord *secret); /* Many of our operations might be slow due to crypto, fsck, recursive chown() and so on. For these - * operations permit a *very* long time-out */ + * operations permit a *very* long timeout */ #define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE) int test_password_one(const char *hashed_password, const char *password); diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c new file mode 100644 index 000000000..b7b2c1a3b --- /dev/null +++ b/src/home/homectl-fido2.c @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#if HAVE_LIBFIDO2 +#include +#endif + +#include "ask-password-api.h" +#include "errno-util.h" +#include "format-table.h" +#include "hexdecoct.h" +#include "homectl-fido2.h" +#include "homectl-pkcs11.h" +#include "libcrypt-util.h" +#include "locale-util.h" +#include "memory-util.h" +#include "random-util.h" +#include "strv.h" + +#if HAVE_LIBFIDO2 +static int add_fido2_credential_id( + JsonVariant **v, + const void *cid, + size_t cid_size) { + + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *escaped = NULL; + int r; + + assert(v); + assert(cid); + + r = base64mem(cid, cid_size, &escaped); + if (r < 0) + return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m"); + + w = json_variant_ref(json_variant_by_key(*v, "fido2HmacCredential")); + if (w) { + r = json_variant_strv(w, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse FIDO2 credential ID list: %m"); + + if (strv_contains(l, escaped)) + return 0; + } + + r = strv_extend(&l, escaped); + if (r < 0) + return log_oom(); + + w = json_variant_unref(w); + r = json_variant_new_array_strv(&w, l); + if (r < 0) + return log_error_errno(r, "Failed to create FIDO2 credential ID JSON: %m"); + + r = json_variant_set_field(v, "fido2HmacCredential", w); + if (r < 0) + return log_error_errno(r, "Failed to update FIDO2 credential ID: %m"); + + return 0; +} + +static int add_fido2_salt( + JsonVariant **v, + const void *cid, + size_t cid_size, + const void *fido2_salt, + size_t fido2_salt_size, + const void *secret, + size_t secret_size) { + + _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL; + _cleanup_(erase_and_freep) char *base64_encoded = NULL; + _cleanup_free_ char *unix_salt = NULL; + struct crypt_data cd = {}; + char *k; + int r; + + r = make_salt(&unix_salt); + if (r < 0) + return log_error_errno(r, "Failed to generate salt: %m"); + + /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends + * expect a NUL terminated string, and we use a binary key */ + r = base64mem(secret, secret_size, &base64_encoded); + if (r < 0) + return log_error_errno(r, "Failed to base64 encode secret key: %m"); + + errno = 0; + k = crypt_r(base64_encoded, unix_salt, &cd); + if (!k) + return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); + + r = json_build(&e, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("credential", JSON_BUILD_BASE64(cid, cid_size)), + JSON_BUILD_PAIR("salt", JSON_BUILD_BASE64(fido2_salt, fido2_salt_size)), + JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k)))); + if (r < 0) + return log_error_errno(r, "Failed to build FIDO2 salt JSON key object: %m"); + + w = json_variant_ref(json_variant_by_key(*v, "privileged")); + l = json_variant_ref(json_variant_by_key(w, "fido2HmacSalt")); + + r = json_variant_append_array(&l, e); + if (r < 0) + return log_error_errno(r, "Failed append FIDO2 salt: %m"); + + r = json_variant_set_field(&w, "fido2HmacSalt", l); + if (r < 0) + return log_error_errno(r, "Failed to set FDO2 salt: %m"); + + r = json_variant_set_field(v, "privileged", w); + if (r < 0) + return log_error_errno(r, "Failed to update privileged field: %m"); + + return 0; +} +#endif + +#define FIDO2_SALT_SIZE 32 + +int identity_add_fido2_parameters( + JsonVariant **v, + const char *device) { + +#if HAVE_LIBFIDO2 + _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL; + _cleanup_(fido_assert_free) fido_assert_t *a = NULL; + _cleanup_(erase_and_freep) char *used_pin = NULL; + _cleanup_(fido_cred_free) fido_cred_t *c = NULL; + _cleanup_(fido_dev_free) fido_dev_t *d = NULL; + _cleanup_(erase_and_freep) void *salt = NULL; + JsonVariant *un, *realm, *rn; + bool found_extension = false; + const void *cid, *secret; + const char *fido_un; + size_t n, cid_size, secret_size; + char **e; + int r; + + /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to + * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account + * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2 + * device never sees the volume key. + * + * S = HMAC-SHA256(I, D) + * + * with: S → LUKS/account authentication key (never stored) + * I → internal key on FIDO2 device (stored in the FIDO2 device) + * D → salt we generate here (stored in the privileged part of the JSON record) + * + */ + + assert(v); + assert(device); + + salt = malloc(FIDO2_SALT_SIZE); + if (!salt) + return log_oom(); + + r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK); + if (r < 0) + return log_error_errno(r, "Failed to generate salt: %m"); + + d = fido_dev_new(); + if (!d) + return log_oom(); + + r = fido_dev_open(d, device); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to open FIDO2 device %s: %s", device, fido_strerr(r)); + + if (!fido_dev_is_fido2(d)) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Specified device %s is not a FIDO2 device.", device); + + di = fido_cbor_info_new(); + if (!di) + return log_oom(); + + r = fido_dev_get_cbor_info(d, di); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get CBOR device info for %s: %s", device, fido_strerr(r)); + + e = fido_cbor_info_extensions_ptr(di); + n = fido_cbor_info_extensions_len(di); + + for (size_t i = 0; i < n; i++) + if (streq(e[i], "hmac-secret")) { + found_extension = true; + break; + } + + if (!found_extension) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device); + + c = fido_cred_new(); + if (!c) + return log_oom(); + + r = fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", fido_strerr(r)); + + r = fido_cred_set_rp(c, "io.systemd.home", "Home Directory"); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 credential relying party ID/name: %s", fido_strerr(r)); + + r = fido_cred_set_type(c, COSE_ES256); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 credential type to ES256: %s", fido_strerr(r)); + + un = json_variant_by_key(*v, "userName"); + if (!un) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "userName field of user record is missing"); + if (!json_variant_is_string(un)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "userName field of user record is not a string"); + + realm = json_variant_by_key(*v, "realm"); + if (realm) { + if (!json_variant_is_string(realm)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "realm field of user record is not a string"); + + fido_un = strjoina(json_variant_string(un), json_variant_string(realm)); + } else + fido_un = json_variant_string(un); + + rn = json_variant_by_key(*v, "realName"); + if (rn && !json_variant_is_string(rn)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "realName field of user record is not a string"); + + r = fido_cred_set_user(c, + (const unsigned char*) fido_un, strlen(fido_un), /* We pass the user ID and name as the same */ + fido_un, + rn ? json_variant_string(rn) : NULL, + NULL /* icon URL */); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 credential user data: %s", fido_strerr(r)); + + r = fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 client data hash: %s", fido_strerr(r)); + + r = fido_cred_set_rk(c, FIDO_OPT_FALSE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to turn off FIDO2 resident key option of credential: %s", fido_strerr(r)); + + r = fido_cred_set_uv(c, FIDO_OPT_FALSE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to turn off FIDO2 user verification option of credential: %s", fido_strerr(r)); + + log_info("Initializing FIDO2 credential on security token."); + + log_notice("%s%s(Hint: This might require verification of user presence on security token.)", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); + + r = fido_dev_make_cred(d, c, NULL); + if (r == FIDO_ERR_PIN_REQUIRED) { + _cleanup_free_ char *text = NULL; + + if (asprintf(&text, "Please enter security token PIN:") < 0) + return log_oom(); + + for (;;) { + _cleanup_(strv_free_erasep) char **pin = NULL; + char **i; + + r = ask_password_auto(text, "user-home", NULL, "fido2-pin", USEC_INFINITY, 0, &pin); + if (r < 0) + return log_error_errno(r, "Failed to acquire user PIN: %m"); + + r = FIDO_ERR_PIN_INVALID; + STRV_FOREACH(i, pin) { + if (isempty(*i)) { + log_info("PIN may not be empty."); + continue; + } + + r = fido_dev_make_cred(d, c, *i); + if (r == FIDO_OK) { + used_pin = strdup(*i); + if (!used_pin) + return log_oom(); + break; + } + if (r != FIDO_ERR_PIN_INVALID) + break; + } + + if (r != FIDO_ERR_PIN_INVALID) + break; + + log_notice("PIN incorrect, please try again."); + } + } + if (r == FIDO_ERR_PIN_AUTH_BLOCKED) + return log_notice_errno(SYNTHETIC_ERRNO(EPERM), + "Token PIN is currently blocked, please remove and reinsert token."); + if (r == FIDO_ERR_ACTION_TIMEOUT) + return log_error_errno(SYNTHETIC_ERRNO(ENOSTR), + "Token action timeout. (User didn't interact with token quickly enough.)"); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to generate FIDO2 credential: %s", fido_strerr(r)); + + cid = fido_cred_id_ptr(c); + if (!cid) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID."); + + cid_size = fido_cred_id_len(c); + + a = fido_assert_new(); + if (!a) + return log_oom(); + + r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r)); + + r = fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r)); + + r = fido_assert_set_rp(a, "io.systemd.home"); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 assertion ID: %s", fido_strerr(r)); + + r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r)); + + r = fido_assert_allow_cred(a, cid, cid_size); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r)); + + r = fido_assert_set_up(a, FIDO_OPT_FALSE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to turn off FIDO2 assertion user presence: %s", fido_strerr(r)); + + log_info("Generating secret key on FIDO2 security token."); + + r = fido_dev_get_assert(d, a, used_pin); + if (r == FIDO_ERR_UP_REQUIRED) { + r = fido_assert_set_up(a, FIDO_OPT_TRUE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to turn on FIDO2 assertion user presence: %s", fido_strerr(r)); + + log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); + + r = fido_dev_get_assert(d, a, used_pin); + } + if (r == FIDO_ERR_ACTION_TIMEOUT) + return log_error_errno(SYNTHETIC_ERRNO(ENOSTR), + "Token action timeout. (User didn't interact with token quickly enough.)"); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to ask token for assertion: %s", fido_strerr(r)); + + secret = fido_assert_hmac_secret_ptr(a, 0); + if (!secret) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret."); + + secret_size = fido_assert_hmac_secret_len(a, 0); + + r = add_fido2_credential_id(v, cid, cid_size); + if (r < 0) + return r; + + r = add_fido2_salt(v, + cid, + cid_size, + salt, + FIDO2_SALT_SIZE, + secret, + secret_size); + if (r < 0) + return r; + + /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed + * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or + * fscrypt. */ + r = identity_add_token_pin(v, used_pin); + if (r < 0) + return r; + + return 0; +#else + return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build."); +#endif +} + +int list_fido2_devices(void) { +#if HAVE_LIBFIDO2 + _cleanup_(table_unrefp) Table *t = NULL; + size_t allocated = 64, found = 0; + fido_dev_info_t *di = NULL; + int r; + + di = fido_dev_info_new(allocated); + if (!di) + return log_oom(); + + r = fido_dev_info_manifest(di, allocated, &found); + if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) { + /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */ + log_info("No FIDO2 devices found."); + r = 0; + goto finish; + } + if (r != FIDO_OK) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r)); + goto finish; + } + + t = table_new("path", "manufacturer", "product"); + if (!t) { + r = log_oom(); + goto finish; + } + + for (size_t i = 0; i < found; i++) { + const fido_dev_info_t *entry; + + entry = fido_dev_info_ptr(di, i); + if (!entry) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get device information for FIDO device %zu.", i); + goto finish; + } + + r = table_add_many( + t, + TABLE_PATH, fido_dev_info_path(entry), + TABLE_STRING, fido_dev_info_manufacturer_string(entry), + TABLE_STRING, fido_dev_info_product_string(entry)); + if (r < 0) { + table_log_add_error(r); + goto finish; + } + } + + r = table_print(t, stdout); + if (r < 0) { + log_error_errno(r, "Failed to show device table: %m"); + goto finish; + } + + r = 0; + +finish: + fido_dev_info_free(&di, allocated); + return r; +#else + return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build."); +#endif +} + +int find_fido2_auto(char **ret) { +#if HAVE_LIBFIDO2 + _cleanup_free_ char *copy = NULL; + size_t di_size = 64, found = 0; + const fido_dev_info_t *entry; + fido_dev_info_t *di = NULL; + const char *path; + int r; + + di = fido_dev_info_new(di_size); + if (!di) + return log_oom(); + + r = fido_dev_info_manifest(di, di_size, &found); + if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) { + /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */ + r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO2 devices found."); + goto finish; + } + if (r != FIDO_OK) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r)); + goto finish; + } + if (found > 1) { + r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO2 device found."); + goto finish; + } + + entry = fido_dev_info_ptr(di, 0); + if (!entry) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get device information for FIDO device 0."); + goto finish; + } + + path = fido_dev_info_path(entry); + if (!path) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to query FIDO device path."); + goto finish; + } + + copy = strdup(path); + if (!copy) { + r = log_oom(); + goto finish; + } + + *ret = TAKE_PTR(copy); + r = 0; + +finish: + fido_dev_info_free(&di, di_size); + return r; +#else + return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build."); +#endif +} diff --git a/src/home/homectl-fido2.h b/src/home/homectl-fido2.h new file mode 100644 index 000000000..0d9faefa8 --- /dev/null +++ b/src/home/homectl-fido2.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "json.h" + +int identity_add_fido2_parameters(JsonVariant **v, const char *device); + +int list_fido2_devices(void); + +int find_fido2_auto(char **ret); diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c new file mode 100644 index 000000000..f4253ed7b --- /dev/null +++ b/src/home/homectl-pkcs11.c @@ -0,0 +1,480 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "errno-util.h" +#include "format-table.h" +#include "hexdecoct.h" +#include "homectl-pkcs11.h" +#include "libcrypt-util.h" +#include "memory-util.h" +#include "openssl-util.h" +#include "pkcs11-util.h" +#include "random-util.h" +#include "strv.h" + +struct pkcs11_callback_data { + char *pin_used; + X509 *cert; +}; + +#if HAVE_P11KIT +static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) { + erase_and_free(data->pin_used); + X509_free(data->cert); +} + +static int pkcs11_callback( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_SLOT_ID slot_id, + const CK_SLOT_INFO *slot_info, + const CK_TOKEN_INFO *token_info, + P11KitUri *uri, + void *userdata) { + + _cleanup_(erase_and_freep) char *pin_used = NULL; + struct pkcs11_callback_data *data = userdata; + CK_OBJECT_HANDLE object; + int r; + + assert(m); + assert(slot_info); + assert(token_info); + assert(uri); + assert(data); + + /* Called for every token matching our URI */ + + r = pkcs11_token_login(m, session, slot_id, token_info, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used); + if (r < 0) + return r; + + r = pkcs11_token_find_x509_certificate(m, session, uri, &object); + if (r < 0) + return r; + + r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert); + if (r < 0) + return r; + + /* Let's read some random data off the token and write it to the kernel pool before we generate our + * random key from it. This way we can claim the quality of the RNG is at least as good as the + * kernel's and the token's pool */ + (void) pkcs11_token_acquire_rng(m, session); + + data->pin_used = TAKE_PTR(pin_used); + return 1; +} +#endif + +static int acquire_pkcs11_certificate( + const char *uri, + X509 **ret_cert, + char **ret_pin_used) { + +#if HAVE_P11KIT + _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {}; + int r; + + r = pkcs11_find_token(uri, pkcs11_callback, &data); + if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */ + return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri); + if (r < 0) + return r; + + *ret_cert = TAKE_PTR(data.cert); + *ret_pin_used = TAKE_PTR(data.pin_used); + + return 0; +#else + return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build."); +#endif +} + +static int encrypt_bytes( + EVP_PKEY *pkey, + const void *decrypted_key, + size_t decrypted_key_size, + void **ret_encrypt_key, + size_t *ret_encrypt_key_size) { + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL; + _cleanup_free_ void *b = NULL; + size_t l; + + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (!ctx) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context"); + + if (EVP_PKEY_encrypt_init(ctx) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context"); + + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding"); + + if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size"); + + b = malloc(l); + if (!b) + return log_oom(); + + if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size"); + + *ret_encrypt_key = TAKE_PTR(b); + *ret_encrypt_key_size = l; + + return 0; +} + +static int add_pkcs11_encrypted_key( + JsonVariant **v, + const char *uri, + const void *encrypted_key, size_t encrypted_key_size, + const void *decrypted_key, size_t decrypted_key_size) { + + _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL; + _cleanup_(erase_and_freep) char *base64_encoded = NULL; + _cleanup_free_ char *salt = NULL; + struct crypt_data cd = {}; + char *k; + int r; + + assert(v); + assert(uri); + assert(encrypted_key); + assert(encrypted_key_size > 0); + assert(decrypted_key); + assert(decrypted_key_size > 0); + + r = make_salt(&salt); + if (r < 0) + return log_error_errno(r, "Failed to generate salt: %m"); + + /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends + * expect a NUL terminated string, and we use a binary key */ + r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); + if (r < 0) + return log_error_errno(r, "Failed to base64 encode secret key: %m"); + + errno = 0; + k = crypt_r(base64_encoded, salt, &cd); + if (!k) + return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); + + r = json_build(&e, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)), + JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)), + JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k)))); + if (r < 0) + return log_error_errno(r, "Failed to build encrypted JSON key object: %m"); + + w = json_variant_ref(json_variant_by_key(*v, "privileged")); + l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey")); + + r = json_variant_append_array(&l, e); + if (r < 0) + return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m"); + + r = json_variant_set_field(&w, "pkcs11EncryptedKey", l); + if (r < 0) + return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m"); + + r = json_variant_set_field(v, "privileged", w); + if (r < 0) + return log_error_errno(r, "Failed to update privileged field: %m"); + + return 0; +} + +static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + _cleanup_strv_free_ char **l = NULL; + int r; + + assert(v); + assert(uri); + + w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri")); + if (w) { + r = json_variant_strv(w, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse PKCS#11 token list: %m"); + + if (strv_contains(l, uri)) + return 0; + } + + r = strv_extend(&l, uri); + if (r < 0) + return log_oom(); + + w = json_variant_unref(w); + r = json_variant_new_array_strv(&w, l); + if (r < 0) + return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m"); + + r = json_variant_set_field(v, "pkcs11TokenUri", w); + if (r < 0) + return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m"); + + return 0; +} + +int identity_add_token_pin(JsonVariant **v, const char *pin) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL; + _cleanup_(strv_free_erasep) char **pins = NULL; + int r; + + assert(v); + + if (isempty(pin)) + return 0; + + w = json_variant_ref(json_variant_by_key(*v, "secret")); + l = json_variant_ref(json_variant_by_key(w, "tokenPin")); + + r = json_variant_strv(l, &pins); + if (r < 0) + return log_error_errno(r, "Failed to convert PIN array: %m"); + + if (strv_find(pins, pin)) + return 0; + + r = strv_extend(&pins, pin); + if (r < 0) + return log_oom(); + + strv_uniq(pins); + + l = json_variant_unref(l); + + r = json_variant_new_array_strv(&l, pins); + if (r < 0) + return log_error_errno(r, "Failed to allocate new PIN array JSON: %m"); + + json_variant_sensitive(l); + + r = json_variant_set_field(&w, "tokenPin", l); + if (r < 0) + return log_error_errno(r, "Failed to update PIN field: %m"); + + r = json_variant_set_field(v, "secret", w); + if (r < 0) + return log_error_errno(r, "Failed to update secret object: %m"); + + return 1; +} + +int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { + _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL; + _cleanup_(erase_and_freep) char *pin = NULL; + size_t decrypted_key_size, encrypted_key_size; + _cleanup_(X509_freep) X509 *cert = NULL; + EVP_PKEY *pkey; + RSA *rsa; + int bits; + int r; + + assert(v); + + r = acquire_pkcs11_certificate(uri, &cert, &pin); + if (r < 0) + return r; + + pkey = X509_get0_pubkey(cert); + if (!pkey) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate."); + + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key."); + + rsa = EVP_PKEY_get0_RSA(pkey); + if (!rsa) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate."); + + bits = RSA_bits(rsa); + log_debug("Bits in RSA key: %i", bits); + + /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only + * generate a random key half the size of the RSA length */ + decrypted_key_size = bits / 8 / 2; + + if (decrypted_key_size < 1) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?"); + + log_debug("Generating %zu bytes random key.", decrypted_key_size); + + decrypted_key = malloc(decrypted_key_size); + if (!decrypted_key) + return log_oom(); + + r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK); + if (r < 0) + return log_error_errno(r, "Failed to generate random key: %m"); + + r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size); + if (r < 0) + return log_error_errno(r, "Failed to encrypt key: %m"); + + /* Add the token URI to the public part of the record. */ + r = add_pkcs11_token_uri(v, uri); + if (r < 0) + return r; + + /* Include the encrypted version of the random key we just generated in the privileged part of the record */ + r = add_pkcs11_encrypted_key( + v, + uri, + encrypted_key, encrypted_key_size, + decrypted_key, decrypted_key_size); + if (r < 0) + return r; + + /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed + * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or + * fscrypt. */ + r = identity_add_token_pin(v, pin); + if (r < 0) + return r; + + return 0; +} + +#if HAVE_P11KIT +static int list_callback( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_SLOT_ID slot_id, + const CK_SLOT_INFO *slot_info, + const CK_TOKEN_INFO *token_info, + P11KitUri *uri, + void *userdata) { + + _cleanup_free_ char *token_uri_string = NULL, *token_label = NULL, *token_manufacturer_id = NULL, *token_model = NULL; + _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL; + Table *t = userdata; + int uri_result, r; + + assert(slot_info); + assert(token_info); + + /* We only care about hardware devices here with a token inserted. Let's filter everything else + * out. (Note that the user can explicitly specify non-hardware tokens if they like, but during + * enumeration we'll filter those, since software tokens are typically the system certificate store + * and such, and it's typically not what people want to bind their home directories to.) */ + if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT)) + return -EAGAIN; + + token_label = pkcs11_token_label(token_info); + if (!token_label) + return log_oom(); + + token_manufacturer_id = pkcs11_token_manufacturer_id(token_info); + if (!token_manufacturer_id) + return log_oom(); + + token_model = pkcs11_token_model(token_info); + if (!token_model) + return log_oom(); + + token_uri = uri_from_token_info(token_info); + if (!token_uri) + return log_oom(); + + uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string); + if (uri_result != P11_KIT_URI_OK) + return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result)); + + r = table_add_many( + t, + TABLE_STRING, token_uri_string, + TABLE_STRING, token_label, + TABLE_STRING, token_manufacturer_id, + TABLE_STRING, token_model); + if (r < 0) + return table_log_add_error(r); + + return -EAGAIN; /* keep scanning */ +} +#endif + +int list_pkcs11_tokens(void) { +#if HAVE_P11KIT + _cleanup_(table_unrefp) Table *t = NULL; + int r; + + t = table_new("uri", "label", "manufacturer", "model"); + if (!t) + return log_oom(); + + r = pkcs11_find_token(NULL, list_callback, t); + if (r < 0 && r != -EAGAIN) + return r; + + if (table_get_rows(t) <= 1) { + log_info("No suitable PKCS#11 tokens found."); + return 0; + } + + r = table_print(t, stdout); + if (r < 0) + return log_error_errno(r, "Failed to show device table: %m"); + + return 0; +#else + return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build."); +#endif +} + +#if HAVE_P11KIT +static int auto_callback( + CK_FUNCTION_LIST *m, + CK_SESSION_HANDLE session, + CK_SLOT_ID slot_id, + const CK_SLOT_INFO *slot_info, + const CK_TOKEN_INFO *token_info, + P11KitUri *uri, + void *userdata) { + + _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL; + char **t = userdata; + int uri_result; + + assert(slot_info); + assert(token_info); + + if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT)) + return -EAGAIN; + + if (*t) + return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), + "More than one suitable PKCS#11 token found."); + + token_uri = uri_from_token_info(token_info); + if (!token_uri) + return log_oom(); + + uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, t); + if (uri_result != P11_KIT_URI_OK) + return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result)); + + return 0; +} +#endif + +int find_pkcs11_token_auto(char **ret) { +#if HAVE_P11KIT + int r; + + r = pkcs11_find_token(NULL, auto_callback, ret); + if (r == -EAGAIN) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No suitable PKCS#11 tokens found."); + if (r < 0) + return r; + + return 0; +#else + return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build."); +#endif +} diff --git a/src/home/homectl-pkcs11.h b/src/home/homectl-pkcs11.h new file mode 100644 index 000000000..0403c73ea --- /dev/null +++ b/src/home/homectl-pkcs11.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "json.h" + +int identity_add_token_pin(JsonVariant **v, const char *pin); + +int identity_add_pkcs11_key_data(JsonVariant **v, const char *token_uri); + +int list_pkcs11_tokens(void); +int find_pkcs11_token_auto(char **ret); diff --git a/src/home/homectl.c b/src/home/homectl.c index 8a346d462..33e262706 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -4,26 +4,22 @@ #include "sd-bus.h" -#include "alloc-util.h" #include "ask-password-api.h" #include "bus-common-errors.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-locator.h" #include "cgroup-util.h" #include "dns-domain.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "format-table.h" -#include "format-util.h" -#include "fs-util.h" -#include "hexdecoct.h" #include "home-util.h" -#include "libcrypt-util.h" +#include "homectl-fido2.h" +#include "homectl-pkcs11.h" #include "locale-util.h" #include "main-func.h" #include "memory-util.h" -#include "openssl-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -31,7 +27,6 @@ #include "pretty-print.h" #include "process-util.h" #include "pwquality-util.h" -#include "random-util.h" #include "rlimit-util.h" #include "spawn-polkit-agent.h" #include "terminal-util.h" @@ -56,6 +51,7 @@ static char **arg_identity_filter_rlimits = NULL; static uint64_t arg_disk_size = UINT64_MAX; static uint64_t arg_disk_size_relative = UINT64_MAX; static char **arg_pkcs11_token_uri = NULL; +static char **arg_fido2_device = NULL; static bool arg_json = false; static JsonFormatFlags arg_json_format_flags = 0; static bool arg_and_resize = false; @@ -73,6 +69,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep); static bool identity_properties_specified(void) { return @@ -83,7 +80,8 @@ static bool identity_properties_specified(void) { !json_variant_is_blank_object(arg_identity_extra_rlimits) || !strv_isempty(arg_identity_filter) || !strv_isempty(arg_identity_filter_rlimits) || - !strv_isempty(arg_pkcs11_token_uri); + !strv_isempty(arg_pkcs11_token_uri) || + !strv_isempty(arg_fido2_device); } static int acquire_bus(sd_bus **bus) { @@ -116,15 +114,7 @@ static int list_homes(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ListHomes", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_home_mgr, "ListHomes", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r)); @@ -152,12 +142,12 @@ static int list_homes(int argc, char *argv[], void *userdata) { TABLE_UID, uid, TABLE_GID, gid); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); r = table_add_cell(table, &cell, TABLE_STRING, state); if (r < 0) - return log_error_errno(r, "Failed to add field to table: %m"); + return table_log_add_error(r); color = user_record_state_color(state); if (color) @@ -168,7 +158,7 @@ static int list_homes(int argc, char *argv[], void *userdata) { TABLE_STRING, home, TABLE_STRING, strna(empty_to_null(shell))); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); } r = sd_bus_message_exit_container(reply); @@ -178,7 +168,7 @@ static int list_homes(int argc, char *argv[], void *userdata) { if (table_get_rows(table) > 1 || arg_json) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) - return log_error_errno(r, "Failed to sort table: %m"); + return table_log_sort_error(r); table_set_header(table, arg_legend); @@ -187,7 +177,7 @@ static int list_homes(int argc, char *argv[], void *userdata) { else r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } if (arg_legend && !arg_json) { @@ -244,7 +234,7 @@ static int acquire_existing_password(const char *user_name, UserRecord *hr, bool return 0; } -static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) { +static int acquire_token_pin(const char *user_name, UserRecord *hr) { _cleanup_(strv_free_erasep) char **pin = NULL; _cleanup_free_ char *question = NULL; char *e; @@ -255,9 +245,9 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) { e = getenv("PIN"); if (e) { - r = user_record_set_pkcs11_pin(hr, STRV_MAKE(e), false); + r = user_record_set_token_pin(hr, STRV_MAKE(e), false); if (r < 0) - return log_error_errno(r, "Failed to store PKCS#11 PIN: %m"); + return log_error_errno(r, "Failed to store token PIN: %m"); string_erase(e); @@ -271,11 +261,11 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) { return log_oom(); /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */ - r = ask_password_auto(question, "user-home", NULL, "pkcs11-pin", USEC_INFINITY, 0, &pin); + r = ask_password_auto(question, "user-home", NULL, "token-pin", USEC_INFINITY, 0, &pin); if (r < 0) return log_error_errno(r, "Failed to acquire security token PIN: %m"); - r = user_record_set_pkcs11_pin(hr, pin, false); + r = user_record_set_token_pin(hr, pin, false); if (r < 0) return log_error_errno(r, "Failed to store security token PIN: %m"); @@ -323,26 +313,38 @@ static int handle_generic_user_record_error( } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) { - r = acquire_pkcs11_pin(user_name, hr); + r = acquire_token_pin(user_name, hr); if (r < 0) return r; } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) { - log_notice("Please authenticate physically on security token."); + log_notice("%s%sPlease authenticate physically on security token.", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true); if (r < 0) return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m"); + } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) { + + log_notice("%s%sAuthentication requires presence verification on security token.", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); + + r = user_record_set_fido2_user_presence_permitted(hr, true); + if (r < 0) + return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m"); + } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) - return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first."); + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"); else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) { log_notice("Security token PIN incorrect, please try again."); - r = acquire_pkcs11_pin(user_name, hr); + r = acquire_token_pin(user_name, hr); if (r < 0) return r; @@ -350,7 +352,7 @@ static int handle_generic_user_record_error( log_notice("Security token PIN incorrect, please try again (only a few tries left!)."); - r = acquire_pkcs11_pin(user_name, hr); + r = acquire_token_pin(user_name, hr); if (r < 0) return r; @@ -358,7 +360,7 @@ static int handle_generic_user_record_error( log_notice("Security token PIN incorrect, please try again (only one try left!)."); - r = acquire_pkcs11_pin(user_name, hr); + r = acquire_token_pin(user_name, hr); if (r < 0) return r; } else @@ -387,13 +389,7 @@ static int activate_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ActivateHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ActivateHome"); if (r < 0) return bus_log_create_error(r); @@ -435,13 +431,7 @@ static int deactivate_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "DeactivateHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "DeactivateHome"); if (r < 0) return bus_log_create_error(r); @@ -548,28 +538,9 @@ static int inspect_home(int argc, char *argv[], void *userdata) { continue; } - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "GetUserRecordByName", - &error, - &reply, - "s", - *i); - } else { - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "GetUserRecordByUID", - &error, - &reply, - "u", - (uint32_t) uid); - } + r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", *i); + } else + r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByUID", &error, &reply, "u", (uint32_t) uid); if (r < 0) { log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r)); @@ -643,13 +614,7 @@ static int authenticate_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "AuthenticateHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "AuthenticateHome"); if (r < 0) return bus_log_create_error(r); @@ -934,336 +899,6 @@ static int add_disposition(JsonVariant **v) { return 1; } -struct pkcs11_callback_data { - char *pin_used; - X509 *cert; -}; - -static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) { - erase_and_free(data->pin_used); - X509_free(data->cert); -} - -#if HAVE_P11KIT -static int pkcs11_callback( - CK_FUNCTION_LIST *m, - CK_SESSION_HANDLE session, - CK_SLOT_ID slot_id, - const CK_SLOT_INFO *slot_info, - const CK_TOKEN_INFO *token_info, - P11KitUri *uri, - void *userdata) { - - _cleanup_(erase_and_freep) char *pin_used = NULL; - struct pkcs11_callback_data *data = userdata; - CK_OBJECT_HANDLE object; - int r; - - assert(m); - assert(slot_info); - assert(token_info); - assert(uri); - assert(data); - - /* Called for every token matching our URI */ - - r = pkcs11_token_login(m, session, slot_id, token_info, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used); - if (r < 0) - return r; - - r = pkcs11_token_find_x509_certificate(m, session, uri, &object); - if (r < 0) - return r; - - r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert); - if (r < 0) - return r; - - /* Let's read some random data off the token and write it to the kernel pool before we generate our - * random key from it. This way we can claim the quality of the RNG is at least as good as the - * kernel's and the token's pool */ - (void) pkcs11_token_acquire_rng(m, session); - - data->pin_used = TAKE_PTR(pin_used); - return 1; -} -#endif - -static int acquire_pkcs11_certificate( - const char *uri, - X509 **ret_cert, - char **ret_pin_used) { - -#if HAVE_P11KIT - _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {}; - int r; - - r = pkcs11_find_token(uri, pkcs11_callback, &data); - if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */ - return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri); - if (r < 0) - return r; - - *ret_cert = TAKE_PTR(data.cert); - *ret_pin_used = TAKE_PTR(data.pin_used); - - return 0; -#else - return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build."); -#endif -} - -static int encrypt_bytes( - EVP_PKEY *pkey, - const void *decrypted_key, - size_t decrypted_key_size, - void **ret_encrypt_key, - size_t *ret_encrypt_key_size) { - - _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL; - _cleanup_free_ void *b = NULL; - size_t l; - - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (!ctx) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context"); - - if (EVP_PKEY_encrypt_init(ctx) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context"); - - if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding"); - - if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size"); - - b = malloc(l); - if (!b) - return log_oom(); - - if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size"); - - *ret_encrypt_key = TAKE_PTR(b); - *ret_encrypt_key_size = l; - - return 0; -} - -static int add_pkcs11_pin(JsonVariant **v, const char *pin) { - _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL; - _cleanup_(strv_free_erasep) char **pins = NULL; - int r; - - assert(v); - - if (isempty(pin)) - return 0; - - w = json_variant_ref(json_variant_by_key(*v, "secret")); - l = json_variant_ref(json_variant_by_key(w, "pkcs11Pin")); - - r = json_variant_strv(l, &pins); - if (r < 0) - return log_error_errno(r, "Failed to convert PIN array: %m"); - - if (strv_find(pins, pin)) - return 0; - - r = strv_extend(&pins, pin); - if (r < 0) - return log_oom(); - - strv_uniq(pins); - - l = json_variant_unref(l); - - r = json_variant_new_array_strv(&l, pins); - if (r < 0) - return log_error_errno(r, "Failed to allocate new PIN array JSON: %m"); - - json_variant_sensitive(l); - - r = json_variant_set_field(&w, "pkcs11Pin", l); - if (r < 0) - return log_error_errno(r, "Failed to update PIN field: %m"); - - r = json_variant_set_field(v, "secret", w); - if (r < 0) - return log_error_errno(r, "Failed to update secret object: %m"); - - return 1; -} - -static int add_pkcs11_encrypted_key( - JsonVariant **v, - const char *uri, - const void *encrypted_key, size_t encrypted_key_size, - const void *decrypted_key, size_t decrypted_key_size) { - - _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL; - _cleanup_(erase_and_freep) char *base64_encoded = NULL; - _cleanup_free_ char *salt = NULL; - struct crypt_data cd = {}; - char *k; - int r; - - assert(v); - assert(uri); - assert(encrypted_key); - assert(encrypted_key_size > 0); - assert(decrypted_key); - assert(decrypted_key_size > 0); - - r = make_salt(&salt); - if (r < 0) - return log_error_errno(r, "Failed to generate salt: %m"); - - /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends - * expect a NUL terminated string, and we use a binary key */ - r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); - if (r < 0) - return log_error_errno(r, "Failed to base64 encode secret key: %m"); - - errno = 0; - k = crypt_r(base64_encoded, salt, &cd); - if (!k) - return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); - - r = json_build(&e, JSON_BUILD_OBJECT( - JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)), - JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)), - JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k)))); - if (r < 0) - return log_error_errno(r, "Failed to build encrypted JSON key object: %m"); - - w = json_variant_ref(json_variant_by_key(*v, "privileged")); - l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey")); - - r = json_variant_append_array(&l, e); - if (r < 0) - return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m"); - - r = json_variant_set_field(&w, "pkcs11EncryptedKey", l); - if (r < 0) - return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m"); - - r = json_variant_set_field(v, "privileged", w); - if (r < 0) - return log_error_errno(r, "Failed to update privileged field: %m"); - - return 0; -} - -static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) { - _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; - _cleanup_strv_free_ char **l = NULL; - int r; - - assert(v); - assert(uri); - - w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri")); - if (w) { - r = json_variant_strv(w, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse PKCS#11 token list: %m"); - - if (strv_contains(l, uri)) - return 0; - } - - r = strv_extend(&l, uri); - if (r < 0) - return log_oom(); - - w = json_variant_unref(w); - r = json_variant_new_array_strv(&w, l); - if (r < 0) - return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m"); - - r = json_variant_set_field(v, "pkcs11TokenUri", w); - if (r < 0) - return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m"); - - return 0; -} - -static int add_pkcs11_key_data(JsonVariant **v, const char *uri) { - _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL; - _cleanup_(erase_and_freep) char *pin = NULL; - size_t decrypted_key_size, encrypted_key_size; - _cleanup_(X509_freep) X509 *cert = NULL; - EVP_PKEY *pkey; - RSA *rsa; - int bits; - int r; - - assert(v); - - r = acquire_pkcs11_certificate(uri, &cert, &pin); - if (r < 0) - return r; - - pkey = X509_get0_pubkey(cert); - if (!pkey) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate."); - - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key."); - - rsa = EVP_PKEY_get0_RSA(pkey); - if (!rsa) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate."); - - bits = RSA_bits(rsa); - log_debug("Bits in RSA key: %i", bits); - - /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only - * generate a random key half the size of the RSA length */ - decrypted_key_size = bits / 8 / 2; - - if (decrypted_key_size < 1) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?"); - - log_debug("Generating %zu bytes random key.", decrypted_key_size); - - decrypted_key = malloc(decrypted_key_size); - if (!decrypted_key) - return log_oom(); - - r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK); - if (r < 0) - return log_error_errno(r, "Failed to generate random key: %m"); - - r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size); - if (r < 0) - return log_error_errno(r, "Failed to encrypt key: %m"); - - /* Add the token URI to the public part of the record. */ - r = add_pkcs11_token_uri(v, uri); - if (r < 0) - return r; - - /* Include the encrypted version of the random key we just generated in the privileged part of the record */ - r = add_pkcs11_encrypted_key( - v, - uri, - encrypted_key, encrypted_key_size, - decrypted_key, decrypted_key_size); - if (r < 0) - return r; - - /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed - * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or - * fscrypt. */ - r = add_pkcs11_pin(v, pin); - if (r < 0) - return r; - - return 0; -} - static int acquire_new_home_record(UserRecord **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; @@ -1291,7 +926,13 @@ static int acquire_new_home_record(UserRecord **ret) { return r; STRV_FOREACH(i, arg_pkcs11_token_uri) { - r = add_pkcs11_key_data(&v, *i); + r = identity_add_pkcs11_key_data(&v, *i); + if (r < 0) + return r; + } + + STRV_FOREACH(i, arg_fido2_device) { + r = identity_add_fido2_parameters(&v, *i); if (r < 0) return r; } @@ -1425,7 +1066,7 @@ static int create_home(int argc, char *argv[], void *userdata) { if (r < 0) return r; - /* Remember the original hashed paswords before we add our own, so that we can return to them later, + /* Remember the original hashed passwords before we add our own, so that we can return to them later, * should the entered password turn out not to be acceptable. */ original_hashed_passwords = strv_copy(hr->hashed_password); if (!original_hashed_passwords) @@ -1468,43 +1109,42 @@ static int create_home(int argc, char *argv[], void *userdata) { r = json_variant_format(hr->json, 0, &formatted); if (r < 0) - return r; + return log_error_errno(r, "Failed to format user record: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "CreateHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "CreateHome"); if (r < 0) return bus_log_create_error(r); + (void) sd_bus_message_sensitive(m); + r = sd_bus_message_append(m, "s", formatted); if (r < 0) return bus_log_create_error(r); r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL); if (r < 0) { - if (!sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) - return log_error_errno(r, "Failed to create user home: %s", bus_error_message(&error, r)); + if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) { + log_error_errno(r, "%s", bus_error_message(&error, r)); + log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)"); - log_error_errno(r, "%s", bus_error_message(&error, r)); - log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)"); + r = user_record_set_hashed_password(hr, original_hashed_passwords); + if (r < 0) + return r; + + r = acquire_new_password(hr->user_name, hr, /* suggest = */ false); + if (r < 0) + return r; + + r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true); + if (r < 0) + return log_error_errno(r, "Failed to hash passwords: %m"); + } else { + r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false); + if (r < 0) + return r; + } } else break; /* done */ - - r = user_record_set_hashed_password(hr, original_hashed_passwords); - if (r < 0) - return r; - - r = acquire_new_password(hr->user_name, hr, /* suggest = */ false); - if (r < 0) - return r; - - r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true); - if (r < 0) - return log_error_errno(r, "Failed to hash passwords: %m"); } return 0; @@ -1525,13 +1165,7 @@ static int remove_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "RemoveHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "RemoveHome"); if (r < 0) return bus_log_create_error(r); @@ -1594,16 +1228,7 @@ static int acquire_updated_home_record( if (!identity_properties_specified()) return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified."); - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "GetUserRecordByName", - &error, - &reply, - "s", - username); + r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username); if (r < 0) return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r)); @@ -1630,14 +1255,20 @@ static int acquire_updated_home_record( return r; STRV_FOREACH(i, arg_pkcs11_token_uri) { - r = add_pkcs11_key_data(&json, *i); + r = identity_add_pkcs11_key_data(&json, *i); + if (r < 0) + return r; + } + + STRV_FOREACH(i, arg_fido2_device) { + r = identity_add_fido2_parameters(&json, *i); if (r < 0) return r; } /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always * override. */ - r = update_last_change(&json, !!arg_pkcs11_token_uri, !arg_identity); + r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity); if (r < 0) return r; @@ -1656,6 +1287,26 @@ static int acquire_updated_home_record( return 0; } +static int home_record_reset_human_interaction_permission(UserRecord *hr) { + int r; + + assert(hr); + + /* When we execute multiple operations one after the other, let's reset the permission to ask the + * user each time, so that if interaction is necessary we will be told so again and thus can print a + * nice message to the user, telling the user so. */ + + r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1); + if (r < 0) + return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m"); + + r = user_record_set_fido2_user_presence_permitted(hr, -1); + if (r < 0) + return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m"); + + return 0; +} + static int update_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; @@ -1684,24 +1335,26 @@ static int update_home(int argc, char *argv[], void *userdata) { if (r < 0) return r; + /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated + * authentication might be confusing. */ + + if (arg_and_resize || arg_and_change_password) + log_info("Updating home directory."); + for (;;) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *formatted = NULL; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "UpdateHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "UpdateHome"); if (r < 0) return bus_log_create_error(r); r = json_variant_format(hr->json, 0, &formatted); if (r < 0) - return r; + return log_error_errno(r, "Failed to format user record: %m"); + + (void) sd_bus_message_sensitive(m); r = sd_bus_message_append(m, "s", formatted); if (r < 0) @@ -1723,20 +1376,17 @@ static int update_home(int argc, char *argv[], void *userdata) { break; } + if (arg_and_resize) + log_info("Resizing home."); + + (void) home_record_reset_human_interaction_permission(hr); + /* Also sync down disk size to underlying LUKS/fscrypt/quota */ while (arg_and_resize) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - log_debug("Resizing"); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ResizeHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ResizeHome"); if (r < 0) return bus_log_create_error(r); @@ -1762,20 +1412,17 @@ static int update_home(int argc, char *argv[], void *userdata) { break; } + if (arg_and_change_password) + log_info("Synchronizing passwords and encryption keys."); + + (void) home_record_reset_human_interaction_permission(hr); + /* Also sync down passwords to underlying LUKS/fscrypt */ while (arg_and_change_password) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - log_debug("Propagating password"); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ChangePasswordHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome"); if (r < 0) return bus_log_create_error(r); @@ -1812,6 +1459,8 @@ static int passwd_home(int argc, char *argv[], void *userdata) { if (arg_pkcs11_token_uri) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'."); + if (arg_fido2_device) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the FIDO2 security token use 'homectl update --fido2-device=…'."); if (identity_properties_specified()) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time."); @@ -1847,13 +1496,7 @@ static int passwd_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ChangePasswordHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome"); if (r < 0) return bus_log_create_error(r); @@ -1932,13 +1575,7 @@ static int resize_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ResizeHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ResizeHome"); if (r < 0) return bus_log_create_error(r); @@ -1975,13 +1612,7 @@ static int lock_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "LockHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "LockHome"); if (r < 0) return bus_log_create_error(r); @@ -2020,13 +1651,7 @@ static int unlock_home(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "UnlockHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "UnlockHome"); if (r < 0) return bus_log_create_error(r); @@ -2089,13 +1714,7 @@ static int with_home(int argc, char *argv[], void *userdata) { return log_oom(); for (;;) { - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "AcquireHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "AcquireHome"); if (r < 0) return bus_log_create_error(r); @@ -2135,16 +1754,7 @@ static int with_home(int argc, char *argv[], void *userdata) { } } - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "GetHomeByName", - &error, - &reply, - "s", - argv[1]); + r = bus_call_method(bus, bus_home_mgr, "GetHomeByName", &error, &reply, "s", argv[1]); if (r < 0) return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r)); @@ -2171,13 +1781,7 @@ static int with_home(int argc, char *argv[], void *userdata) { /* Close the fd that pings the home now. */ acquired_fd = safe_close(acquired_fd); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ReleaseHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome"); if (r < 0) return bus_log_create_error(r); @@ -2206,13 +1810,7 @@ static int lock_all_homes(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "LockAllHomes"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "LockAllHomes"); if (r < 0) return bus_log_create_error(r); @@ -2313,6 +1911,8 @@ static int help(int argc, char *argv[], void *userdata) { " Specify SSH public keys\n" " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n" " private key and matching X.509 certificate\n" + " --fido2-device=PATH Path to FIDO2 hidraw device with hmac-secret\n" + " extension\n" "\n%4$sAccount Management User Record Properties:%5$s\n" " --locked=BOOL Set locked account state\n" " --not-before=TIMESTAMP Do not allow logins before\n" @@ -2357,6 +1957,9 @@ static int help(int argc, char *argv[], void *userdata) { " --fs-type=TYPE File system type to use in case of luks\n" " storage (ext4, xfs, btrfs)\n" " --luks-discard=BOOL Whether to use 'discard' feature of file system\n" + " when activated (mounted)\n" + " --luks-offline-discard=BOOL\n" + " Whether to trim file on logout\n" " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n" " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n" " --luks-volume-key-size=BITS\n" @@ -2410,6 +2013,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_IMAGE_PATH, ARG_UMASK, ARG_LUKS_DISCARD, + ARG_LUKS_OFFLINE_DISCARD, ARG_JSON, ARG_SETENV, ARG_TIMEZONE, @@ -2455,6 +2059,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_EXPORT_FORMAT, ARG_AUTO_LOGIN, ARG_PKCS11_TOKEN_URI, + ARG_FIDO2_DEVICE, ARG_AND_RESIZE, ARG_AND_CHANGE_PASSWORD, }; @@ -2503,6 +2108,7 @@ static int parse_argv(int argc, char *argv[]) { { "image-path", required_argument, NULL, ARG_IMAGE_PATH }, { "fs-type", required_argument, NULL, ARG_FS_TYPE }, { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD }, + { "luks-offline-discard", required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD }, { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER }, { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE }, { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE }, @@ -2531,6 +2137,7 @@ static int parse_argv(int argc, char *argv[]) { { "json", required_argument, NULL, ARG_JSON }, { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT }, { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI }, + { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE }, { "and-resize", required_argument, NULL, ARG_AND_RESIZE }, { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD }, {} @@ -2896,7 +2503,7 @@ static int parse_argv(int argc, char *argv[]) { r = json_variant_set_field(&arg_identity_extra, "environment", ne); if (r < 0) - return log_error_errno(r, "Failed to set environent list: %m"); + return log_error_errno(r, "Failed to set environment list: %m"); break; } @@ -2932,6 +2539,9 @@ static int parse_argv(int argc, char *argv[]) { if (!locale_is_valid(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", optarg); + if (locale_is_installed(optarg) <= 0) + log_warning("Locale '%s' is not installed, accepting anyway.", optarg); + r = json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", optarg); if (r < 0) return log_error_errno(r, "Failed to set preferredLanguage field: %m"); @@ -3072,6 +2682,25 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_LUKS_OFFLINE_DISCARD: + if (isempty(optarg)) { + r = drop_from_identity("luksOfflineDiscard"); + if (r < 0) + return r; + + break; + } + + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg); + + r = json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r); + if (r < 0) + return log_error_errno(r, "Failed to set offline discard field: %m"); + + break; + case ARG_LUKS_VOLUME_KEY_SIZE: case ARG_LUKS_PBKDF_PARALLEL_THREADS: case ARG_RATE_LIMIT_BURST: { @@ -3469,6 +3098,9 @@ static int parse_argv(int argc, char *argv[]) { case ARG_PKCS11_TOKEN_URI: { const char *p; + if (streq(optarg, "list")) + return list_pkcs11_tokens(); + /* If --pkcs11-token-uri= is specified we always drop everything old */ FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") { r = drop_from_identity(p); @@ -3481,10 +3113,19 @@ static int parse_argv(int argc, char *argv[]) { break; } - if (!pkcs11_uri_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg); + if (streq(optarg, "auto")) { + _cleanup_free_ char *found = NULL; - r = strv_extend(&arg_pkcs11_token_uri, optarg); + r = find_pkcs11_token_auto(&found); + if (r < 0) + return r; + r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found)); + } else { + if (!pkcs11_uri_valid(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg); + + r = strv_extend(&arg_pkcs11_token_uri, optarg); + } if (r < 0) return r; @@ -3492,6 +3133,41 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_FIDO2_DEVICE: { + const char *p; + + if (streq(optarg, "list")) + return list_fido2_devices(); + + FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") { + r = drop_from_identity(p); + if (r < 0) + return r; + } + + if (isempty(optarg)) { + arg_fido2_device = strv_free(arg_fido2_device); + break; + } + + if (streq(optarg, "auto")) { + _cleanup_free_ char *found = NULL; + + r = find_fido2_auto(&found); + if (r < 0) + return r; + + r = strv_consume(&arg_fido2_device, TAKE_PTR(found)); + } else + r = strv_extend(&arg_fido2_device, optarg); + + if (r < 0) + return r; + + strv_uniq(arg_fido2_device); + break; + } + case 'j': arg_json = true; arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO; @@ -3562,7 +3238,7 @@ static int parse_argv(int argc, char *argv[]) { } } - if (!strv_isempty(arg_pkcs11_token_uri)) + if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device)) arg_and_change_password = true; if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX) @@ -3593,9 +3269,7 @@ static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/home/homed-conf.c b/src/home/homed-conf.c new file mode 100644 index 000000000..df3a17358 --- /dev/null +++ b/src/home/homed-conf.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "conf-parser.h" +#include "def.h" +#include "home-util.h" +#include "homed-conf.h" + +int manager_parse_config_file(Manager *m) { + int r; + + assert(m); + + r = config_parse_many_nulstr( + PKGSYSCONFDIR "/homed.conf", + CONF_PATHS_NULSTR("systemd/homed.conf.d"), + "Home\0", + config_item_perf_lookup, homed_gperf_lookup, + CONFIG_PARSE_WARN, + m, + NULL); + if (r < 0) + return r; + + return 0; + +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_default_storage, user_storage, UserStorage, "Failed to parse default storage setting"); + +int config_parse_default_file_system_type( + 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 (!isempty(rvalue) && !supported_fstype(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unsupported file system, ignoring: %s", rvalue); + return 0; + } + + return free_and_strdup_warn(s, empty_to_null(rvalue)); + +} diff --git a/src/home/homed-conf.h b/src/home/homed-conf.h new file mode 100644 index 000000000..00eb3fdb6 --- /dev/null +++ b/src/home/homed-conf.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "conf-parser.h" +#include "homed-manager.h" + +int manager_parse_config_file(Manager *m); + +const struct ConfigPerfItem* homed_gperf_lookup(const char *key, GPERF_LEN_TYPE length); + +CONFIG_PARSER_PROTOTYPE(config_parse_default_storage); +CONFIG_PARSER_PROTOTYPE(config_parse_default_file_system_type); diff --git a/src/home/homed-gperf.gperf b/src/home/homed-gperf.gperf new file mode 100644 index 000000000..970da5f79 --- /dev/null +++ b/src/home/homed-gperf.gperf @@ -0,0 +1,21 @@ +%{ +#if __GNUC__ >= 7 +_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") +#endif +#include +#include "conf-parser.h" +#include "homed-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name homed_gperf_hash +%define lookup-function-name homed_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Home.DefaultStorage, config_parse_default_storage, 0, offsetof(Manager, default_storage) +Home.DefaultFileSystemType, config_parse_default_file_system_type, 0, offsetof(Manager, default_file_system_type) diff --git a/src/home/homed-home-bus.c b/src/home/homed-home-bus.c index 6b4fa58a6..6be361a5a 100644 --- a/src/home/homed-home-bus.c +++ b/src/home/homed-home-bus.c @@ -712,38 +712,13 @@ int bus_home_method_release( /* We map a uid_t as uint32_t bus property, let's ensure this is safe. */ assert_cc(sizeof(uid_t) == sizeof(uint32_t)); -const sd_bus_vtable home_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("UserName", "s", NULL, offsetof(Home, user_name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Home, uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("UnixRecord", "(suusss)", property_get_unix_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("UserRecord", "(sb)", property_get_user_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Activate", "s", NULL, bus_home_method_activate, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0), - SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Realize", "s", NULL, bus_home_method_realize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Fixate", "s", NULL, bus_home_method_fixate, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Authenticate", "s", NULL, bus_home_method_authenticate, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Update", "s", NULL, bus_home_method_update, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Resize", "ts", NULL, bus_home_method_resize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("ChangePassword", "ss", NULL, bus_home_method_change_password, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0), - SD_BUS_METHOD("Unlock", "s", NULL, bus_home_method_unlock, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Acquire", "sb", "h", bus_home_method_acquire, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("Ref", "b", "h", bus_home_method_ref, 0), - SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0), - SD_BUS_VTABLE_END -}; - int bus_home_path(Home *h, char **ret) { assert(ret); return sd_bus_path_encode("/org/freedesktop/home1/home", h->user_name, ret); } -int bus_home_object_find( +static int bus_home_object_find( sd_bus *bus, const char *path, const char *interface, @@ -772,7 +747,7 @@ int bus_home_object_find( return 1; } -int bus_home_node_enumerator( +static int bus_home_node_enumerator( sd_bus *bus, const char *path, void *userdata, @@ -802,6 +777,107 @@ int bus_home_node_enumerator( return 1; } +const sd_bus_vtable home_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("UserName", "s", + NULL, offsetof(Home, user_name), + SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UID", "u", + NULL, offsetof(Home, uid), + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("UnixRecord", "(suusss)", + property_get_unix_record, 0, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", + property_get_state, 0, + 0), + SD_BUS_PROPERTY("UserRecord", "(sb)", + property_get_user_record, 0, + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE), + + SD_BUS_METHOD_WITH_NAMES("Activate", + "s", + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_activate, + SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0), + SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Realize", + "s", + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_realize, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Fixate", + "s", + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_fixate, + SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("Authenticate", + "s", + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_authenticate, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("Update", + "s", + SD_BUS_PARAM(user_record), + NULL,, + bus_home_method_update, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("Resize", + "ts", + SD_BUS_PARAM(size) + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_resize, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("ChangePassword", + "ss", + SD_BUS_PARAM(new_secret) + SD_BUS_PARAM(old_secret), + NULL,, + bus_home_method_change_password, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0), + SD_BUS_METHOD_WITH_NAMES("Unlock", + "s", + SD_BUS_PARAM(secret), + NULL,, + bus_home_method_unlock, + SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("Acquire", + "sb", + SD_BUS_PARAM(secret) + SD_BUS_PARAM(please_suspend), + "h", + SD_BUS_PARAM(send_fd), + bus_home_method_acquire, + SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("Ref", + "b", + SD_BUS_PARAM(please_suspend), + "h", + SD_BUS_PARAM(send_fd), + bus_home_method_ref, + 0), + SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0), + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation home_object = { + "/org/freedesktop/home1/home", + "org.freedesktop.home1.Home", + .fallback_vtables = BUS_FALLBACK_VTABLES({home_vtable, bus_home_object_find}), + .node_enumerator = bus_home_node_enumerator, + .manager = true, +}; + static int on_deferred_change(sd_event_source *s, void *userdata) { _cleanup_free_ char *path = NULL; Home *h = userdata; diff --git a/src/home/homed-home-bus.h b/src/home/homed-home-bus.h index 20516b120..8ac7ff999 100644 --- a/src/home/homed-home-bus.h +++ b/src/home/homed-home-bus.h @@ -3,6 +3,7 @@ #include "sd-bus.h" +#include "bus-object.h" #include "homed-home.h" int bus_home_client_is_trusted(Home *h, sd_bus_message *message); @@ -25,12 +26,9 @@ int bus_home_method_acquire(sd_bus_message *message, void *userdata, sd_bus_erro int bus_home_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_home_method_release(sd_bus_message *message, void *userdata, sd_bus_error *error); -extern const sd_bus_vtable home_vtable[]; +extern const BusObjectImplementation home_object; int bus_home_path(Home *h, char **ret); -int bus_home_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int bus_home_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); - int bus_home_emit_change(Home *h); int bus_home_emit_remove(Home *h); diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 3ec47ee5d..f0c157cb7 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -292,7 +292,7 @@ int home_save_record(Home *h) { fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity"); - r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600); + r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600|WRITE_STRING_FILE_SYNC); if (r < 0) return r; @@ -360,12 +360,10 @@ static int home_parse_worker_stdout(int _fd, UserRecord **ret) { if (lseek(fd, SEEK_SET, 0) == (off_t) -1) return log_error_errno(errno, "Failed to seek to beginning of memfd: %m"); - f = fdopen(fd, "r"); + f = take_fdopen(&fd, "r"); if (!f) return log_error_errno(errno, "Failed to reopen memfd: %m"); - TAKE_FD(fd); - if (DEBUG_LOGGING) { _cleanup_free_ char *text = NULL; @@ -438,9 +436,9 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) { switch (e) { case -EMSGSIZE: - return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrinked"); + return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk"); case -ETXTBSY: - return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrinked offline"); + return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline"); case -ERANGE: return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small"); case -ENOLINK: @@ -459,6 +457,10 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) { return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required."); case -ERFKILL: return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path."); + case -EMEDIUMTYPE: + return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence."); + case -ENOSTR: + return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)"); case -EOWNERDEAD: return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked."); case -ENOLCK: @@ -473,6 +475,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) { return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name); case -ENOSPC: return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name); + case -EKEYREVOKED: + return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name); } return 0; @@ -1004,6 +1008,8 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord if (r < 0) return r; if (r == 0) { + const char *homework; + /* Child */ if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) { @@ -1011,6 +1017,18 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord _exit(EXIT_FAILURE); } + if (h->manager->default_storage >= 0) + if (setenv("SYSTEMD_HOME_DEFAULT_STORAGE", user_storage_to_string(h->manager->default_storage), 1) < 0) { + log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_STORAGE: %m"); + _exit(EXIT_FAILURE); + } + + if (h->manager->default_file_system_type) + if (setenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE", h->manager->default_file_system_type, 1) < 0) { + log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE: %m"); + _exit(EXIT_FAILURE); + } + r = rearrange_stdio(stdin_fd, stdout_fd, STDERR_FILENO); if (r < 0) { log_error_errno(r, "Failed to rearrange stdin/stdout/stderr: %m"); @@ -1019,7 +1037,11 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */ - execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL); + /* Allow overriding the homework path via an environment variable, to make debugging + * easier. */ + homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH; + + execl(homework, homework, verb, NULL); log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m"); _exit(EXIT_FAILURE); } @@ -1339,7 +1361,13 @@ static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_bind return 0; } -static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) { +static int home_update_internal( + Home *h, + const char *verb, + UserRecord *hr, + UserRecord *secret, + sd_bus_error *error) { + _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL; int r, c; @@ -1523,7 +1551,7 @@ static int home_may_change_password( r = user_record_test_password_change_required(h->record); if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED)) - return 0; /* expired in some form, but chaning is allowed */ + return 0; /* expired in some form, but changing is allowed */ if (IN_SET(r, -EKEYREJECTED, -EROFS)) return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name); if (r < 0) @@ -1750,7 +1778,7 @@ void home_process_notify(Home *h, char **l) { r = safe_atoi(e, &error); if (r < 0) { - log_debug_errno(r, "Failed to parse receieved error number, ignoring: %s", e); + log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e); return; } if (error <= 0) { @@ -2348,7 +2376,7 @@ static int home_dispatch_release(Home *h, Operation *o) { case HOME_UNFIXATED: case HOME_ABSENT: case HOME_INACTIVE: - r = 0; /* done */ + r = 1; /* done */ break; case HOME_LOCKED: @@ -2368,7 +2396,7 @@ static int home_dispatch_release(Home *h, Operation *o) { assert(!h->current_operation); - if (r <= 0) /* failure or completed */ + if (r != 0) /* failure or completed */ operation_result(o, r, &error); else /* ongoing */ h->current_operation = operation_ref(o); @@ -2390,12 +2418,12 @@ static int home_dispatch_lock_all(Home *h, Operation *o) { case HOME_ABSENT: case HOME_INACTIVE: log_info("Home %s is not active, no locking necessary.", h->user_name); - r = 0; /* done */ + r = 1; /* done */ break; case HOME_LOCKED: log_info("Home %s is already locked.", h->user_name); - r = 0; /* done */ + r = 1; /* done */ break; case HOME_ACTIVE: diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index 34a7b4945..ce6919a1a 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -600,44 +600,210 @@ static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus return sd_bus_reply_method_return(message, NULL); } -const sd_bus_vtable manager_vtable[] = { +static const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_METHOD("GetHomeByName", "s", "usussso", method_get_home_by_name, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetHomeByUID", "u", "ssussso", method_get_home_by_uid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUserRecordByName", "s", "sbo", method_get_user_record_by_name, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("GetUserRecordByUID", "u", "sbo", method_get_user_record_by_uid, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("ListHomes", NULL, "a(susussso)", method_list_homes, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetHomeByName", + "s", + SD_BUS_PARAM(user_name), + "usussso", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(home_state) + SD_BUS_PARAM(gid) + SD_BUS_PARAM(real_name) + SD_BUS_PARAM(home_directory) + SD_BUS_PARAM(shell) + SD_BUS_PARAM(bus_path), + method_get_home_by_name, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetHomeByUID", + "u", + SD_BUS_PARAM(uid), + "ssussso", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(home_state) + SD_BUS_PARAM(gid) + SD_BUS_PARAM(real_name) + SD_BUS_PARAM(home_directory) + SD_BUS_PARAM(shell) + SD_BUS_PARAM(bus_path), + method_get_home_by_uid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUserRecordByName", + "s", + SD_BUS_PARAM(user_name), + "sbo", + SD_BUS_PARAM(user_record) + SD_BUS_PARAM(incomplete) + SD_BUS_PARAM(bus_path), + method_get_user_record_by_name, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("GetUserRecordByUID", + "u", + SD_BUS_PARAM(uid), + "sbo", + SD_BUS_PARAM(user_record) + SD_BUS_PARAM(incomplete) + SD_BUS_PARAM(bus_path), + method_get_user_record_by_uid, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("ListHomes", + NULL,, + "a(susussso)", + SD_BUS_PARAM(home_areas), + method_list_homes, + SD_BUS_VTABLE_UNPRIVILEGED), - /* The following methods directly execute an operation on a home area, without ref-counting, queing + /* The following methods directly execute an operation on a home area, without ref-counting, queueing * or anything, and are accessible through homectl. */ - SD_BUS_METHOD("ActivateHome", "ss", NULL, method_activate_home, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("DeactivateHome", "s", NULL, method_deactivate_home, 0), - SD_BUS_METHOD("RegisterHome", "s", NULL, method_register_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Add JSON record to homed, but don't create actual $HOME */ - SD_BUS_METHOD("UnregisterHome", "s", NULL, method_unregister_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Remove JSON record from homed, but don't remove actual $HOME */ - SD_BUS_METHOD("CreateHome", "s", NULL, method_create_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Add JSON record, and create $HOME for it */ - SD_BUS_METHOD("RealizeHome", "ss", NULL, method_realize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Create $HOME for already registered JSON entry */ - SD_BUS_METHOD("RemoveHome", "s", NULL, method_remove_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Remove JSON record and remove $HOME */ - SD_BUS_METHOD("FixateHome", "ss", NULL, method_fixate_home, SD_BUS_VTABLE_SENSITIVE), /* Investigate $HOME and propagate contained JSON record into our database */ - SD_BUS_METHOD("AuthenticateHome", "ss", NULL, method_authenticate_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Just check credentials */ - SD_BUS_METHOD("UpdateHome", "s", NULL, method_update_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Update JSON record of existing user */ - SD_BUS_METHOD("ResizeHome", "sts", NULL, method_resize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("ChangePasswordHome", "sss", NULL, method_change_password_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("LockHome", "s", NULL, method_lock_home, 0), /* Prepare active home for system suspend: flush out passwords, suspend access */ - SD_BUS_METHOD("UnlockHome", "ss", NULL, method_unlock_home, SD_BUS_VTABLE_SENSITIVE), /* Make $HOME usable after system resume again */ + SD_BUS_METHOD_WITH_NAMES("ActivateHome", + "ss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret), + NULL,, + method_activate_home, + SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("DeactivateHome", + "s", + SD_BUS_PARAM(user_name), + NULL,, + method_deactivate_home, + 0), - /* The following methods implement ref-counted activation, and are what the PAM module calls (and - * what "homectl with" runs). In contrast to the methods above which fail if an operation is already - * being executed on a home directory, these ones will queue the request, and are thus more - * reliable. Moreover, they are a bit smarter: AcquireHome() will fixate, activate, unlock, or - * authenticate depending on the state of the home, so that the end result is always the same - * (i.e. the home directory is accessible), and we always validate the specified passwords. RefHome() - * will not authenticate, and thus only works if home is already active. */ - SD_BUS_METHOD("AcquireHome", "ssb", "h", method_acquire_home, SD_BUS_VTABLE_SENSITIVE), - SD_BUS_METHOD("RefHome", "sb", "h", method_ref_home, 0), - SD_BUS_METHOD("ReleaseHome", "s", NULL, method_release_home, 0), + /* Add the JSON record to homed, but don't create actual $HOME */ + SD_BUS_METHOD_WITH_NAMES("RegisterHome", + "s", + SD_BUS_PARAM(user_record), + NULL,, + method_register_home, + SD_BUS_VTABLE_UNPRIVILEGED), + + /* Remove the JSON record from homed, but don't remove actual $HOME */ + SD_BUS_METHOD_WITH_NAMES("UnregisterHome", + "s", + SD_BUS_PARAM(user_name), + NULL,, + method_unregister_home, + SD_BUS_VTABLE_UNPRIVILEGED), + + /* Add JSON record, and create $HOME for it */ + SD_BUS_METHOD_WITH_NAMES("CreateHome", + "s", + SD_BUS_PARAM(user_record), + NULL,, + method_create_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + /* Create $HOME for already registered JSON entry */ + SD_BUS_METHOD_WITH_NAMES("RealizeHome", + "ss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret), + NULL,, + method_realize_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + /* Remove the JSON record and remove $HOME */ + SD_BUS_METHOD_WITH_NAMES("RemoveHome", + "s", + SD_BUS_PARAM(user_name), + NULL,, + method_remove_home, + SD_BUS_VTABLE_UNPRIVILEGED), + + /* Investigate $HOME and propagate contained JSON record into our database */ + SD_BUS_METHOD_WITH_NAMES("FixateHome", + "ss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret), + NULL,, + method_fixate_home, + SD_BUS_VTABLE_SENSITIVE), + + /* Just check credentials */ + SD_BUS_METHOD_WITH_NAMES("AuthenticateHome", + "ss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret), + NULL,, + method_authenticate_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + /* Update the JSON record of existing user */ + SD_BUS_METHOD_WITH_NAMES("UpdateHome", + "s", + SD_BUS_PARAM(user_record), + NULL,, + method_update_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + SD_BUS_METHOD_WITH_NAMES("ResizeHome", + "sts", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(size) + SD_BUS_PARAM(secret), + NULL,, + method_resize_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + SD_BUS_METHOD_WITH_NAMES("ChangePasswordHome", + "sss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(new_secret) + SD_BUS_PARAM(old_secret), + NULL,, + method_change_password_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + + /* Prepare active home for system suspend: flush out passwords, suspend access */ + SD_BUS_METHOD_WITH_NAMES("LockHome", + "s", + SD_BUS_PARAM(user_name), + NULL,, + method_lock_home, + 0), + + /* Make $HOME usable after system resume again */ + SD_BUS_METHOD_WITH_NAMES("UnlockHome", + "ss", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret), + NULL,, + method_unlock_home, + SD_BUS_VTABLE_SENSITIVE), + + /* The following methods implement ref-counted activation, and are what the PAM module and "homectl + * with" use. In contrast to the methods above which fail if an operation is already being executed + * on a home directory, these ones will queue the request, and are thus more reliable. Moreover, + * they are a bit smarter: AcquireHome() will fixate, activate, unlock, or authenticate depending on + * the state of the home area, so that the end result is always the same (i.e. the home directory is + * accessible), and we always validate the specified passwords. RefHome() will not authenticate, and + * thus only works if the home area is already active. */ + SD_BUS_METHOD_WITH_NAMES("AcquireHome", + "ssb", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(secret) + SD_BUS_PARAM(please_suspend), + "h", + SD_BUS_PARAM(send_fd), + method_acquire_home, + SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), + SD_BUS_METHOD_WITH_NAMES("RefHome", + "sb", + SD_BUS_PARAM(user_name) + SD_BUS_PARAM(please_suspend), + "h", + SD_BUS_PARAM(send_fd), + method_ref_home, + 0), + SD_BUS_METHOD_WITH_NAMES("ReleaseHome", + "s", + SD_BUS_PARAM(user_name), + NULL,, + method_release_home, + 0), /* An operation that acts on all homes that allow it */ SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0), @@ -645,6 +811,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_END }; +const BusObjectImplementation manager_object = { + "/org/freedesktop/home1", + "org.freedesktop.home1.Manager", + .vtables = BUS_VTABLES(manager_vtable), + .children = BUS_IMPLEMENTATIONS(&home_object), +}; + static int on_deferred_auto_login(sd_event_source *s, void *userdata) { Manager *m = userdata; int r; diff --git a/src/home/homed-manager-bus.h b/src/home/homed-manager-bus.h index 40e1cc3d8..93bef9df8 100644 --- a/src/home/homed-manager-bus.h +++ b/src/home/homed-manager-bus.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include "sd-bus.h" +#include "bus-util.h" -extern const sd_bus_vtable manager_vtable[]; +extern const BusObjectImplementation manager_object; diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index b1153f623..7d951bee3 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -12,6 +12,7 @@ #include "btrfs-util.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "clean-ipc.h" #include "conf-files.h" @@ -23,6 +24,7 @@ #include "fs-util.h" #include "gpt.h" #include "home-util.h" +#include "homed-conf.h" #include "homed-home-bus.h" #include "homed-home.h" #include "homed-manager-bus.h" @@ -183,10 +185,18 @@ int manager_new(Manager **ret) { assert(ret); - m = new0(Manager, 1); + m = new(Manager, 1); if (!m) return -ENOMEM; + *m = (Manager) { + .default_storage = _USER_STORAGE_INVALID, + }; + + r = manager_parse_config_file(m); + if (r < 0) + return r; + r = sd_event_default(&m->event); if (r < 0) return r; @@ -250,6 +260,8 @@ Manager* manager_free(Manager *m) { varlink_server_unref(m->varlink_server); + free(m->default_file_system_type); + return mfree(m); } @@ -317,21 +329,36 @@ static int manager_add_home_by_record( _cleanup_(user_record_unrefp) UserRecord *hr = NULL; unsigned line, column; int r, is_signed; + struct stat st; Home *h; assert(m); assert(name); assert(fname); + if (fstatat(dir_fd, fname, &st, 0) < 0) + return log_error_errno(errno, "Failed to stat identity record %s: %m", fname); + + if (!S_ISREG(st.st_mode)) { + log_debug("Identity record file %s is not a regular file, ignoring.", fname); + return 0; + } + + if (st.st_size == 0) + goto unlink_this_file; + r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column); if (r < 0) return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column); + if (json_variant_is_blank_object(v)) + goto unlink_this_file; + hr = user_record_new(); if (!hr) return log_oom(); - r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET); + r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG); if (r < 0) return r; @@ -367,7 +394,7 @@ static int manager_add_home_by_record( /* If we acquired a record now for a previously unallocated entry, then reset the state. This * makes sure home_get_state() will check for the availability of the image file dynamically - * in order to detect to distuingish HOME_INACTIVE and HOME_ABSENT. */ + * in order to detect to distinguish HOME_INACTIVE and HOME_ABSENT. */ if (h->state == HOME_UNFIXATED) h->state = _HOME_STATE_INVALID; } else { @@ -382,6 +409,19 @@ static int manager_add_home_by_record( h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE; return 1; + +unlink_this_file: + /* If this is an empty file, then let's just remove it. An empty file is not useful in any case, and + * apparently xfs likes to leave empty files around when not unmounted cleanly (see + * https://github.com/systemd/systemd/issues/15178 for example). Note that we don't delete non-empty + * files even if they are invalid, because that's just too risky, we might delete data the user still + * needs. But empty files are never useful, hence let's just remove them. */ + + if (unlinkat(dir_fd, fname, 0) < 0) + return log_error_errno(errno, "Failed to remove empty user record file %s: %m", fname); + + log_notice("Discovered empty user record file /var/lib/systemd/home/%s, removed automatically.", fname); + return 0; } static int manager_enumerate_records(Manager *m) { @@ -473,7 +513,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) { if (ERRNO_IS_NOT_SUPPORTED(r)) log_debug_errno(r, "No UID quota support on %s, ignoring.", where); else - log_warning_errno(r, "Failed to query quota on %s, ignoring.", where); + log_warning_errno(r, "Failed to query quota on %s, ignoring: %m", where); continue; } @@ -878,21 +918,9 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/home1", "org.freedesktop.home1.Manager", manager_vtable, m); + r = bus_add_implementation(m->bus, &manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/home1/home", "org.freedesktop.home1.Home", home_vtable, bus_home_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add image object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/home1/home", bus_home_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add image enumerator: %m"); - - r = sd_bus_add_object_manager(m->bus, NULL, "/org/freedesktop/home1/home"); - if (r < 0) - return log_error_errno(r, "Failed to add object manager: %m"); + return r; r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.home1", 0, NULL, NULL); if (r < 0) @@ -957,10 +985,7 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) { return -ENOMEM; if (ret_sender) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; bool found_ucred = false; struct cmsghdr *cmsg; struct msghdr mh; @@ -1042,7 +1067,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void * h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid)); if (!h) { - log_warning("Recieved notify datagram of unknown process, ignoring."); + log_warning("Received notify datagram of unknown process, ignoring."); return 0; } @@ -1312,7 +1337,7 @@ static int manager_generate_key_pair(Manager *m) { if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key."); - r = fflush_and_check(fpublic); + r = fflush_sync_and_check(fpublic); if (r < 0) return log_error_errno(r, "Failed to write private key: %m"); @@ -1326,7 +1351,7 @@ static int manager_generate_key_pair(Manager *m) { if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair."); - r = fflush_and_check(fprivate); + r = fflush_sync_and_check(fprivate); if (r < 0) return log_error_errno(r, "Failed to write private key: %m"); @@ -1340,10 +1365,14 @@ static int manager_generate_key_pair(Manager *m) { if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) { (void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */ - return log_error_errno(errno, "Failed to move privtate key file into place: %m"); + return log_error_errno(errno, "Failed to move private key file into place: %m"); } temp_private = mfree(temp_private); + r = fsync_path_at(AT_FDCWD, "/var/lib/systemd/home/"); + if (r < 0) + log_warning_errno(r, "Failed to sync /var/lib/systemd/home/, ignoring: %m"); + return 1; } @@ -1656,7 +1685,7 @@ int manager_enqueue_gc(Manager *m, Home *focus) { return 0; } else - m->gc_focus = focus; /* start focussed */ + m->gc_focus = focus; /* start focused */ r = sd_event_add_defer(m->event, &m->deferred_gc_event_source, on_deferred_gc, m); if (r < 0) diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h index 00298a3d2..83a714462 100644 --- a/src/home/homed-manager.h +++ b/src/home/homed-manager.h @@ -28,10 +28,12 @@ struct Manager { Hashmap *homes_by_sysfs; bool scan_slash_home; + UserStorage default_storage; + char *default_file_system_type; sd_event_source *inotify_event_source; - /* An even source we receieve sd_notify() messages from our worker from */ + /* An event source we receive sd_notify() messages from our worker from */ sd_event_source *notify_socket_event_source; sd_device_monitor *device_monitor; diff --git a/src/home/homed.c b/src/home/homed.c index ca4355826..ed8404d7e 100644 --- a/src/home/homed.c +++ b/src/home/homed.c @@ -3,23 +3,31 @@ #include #include +#include "bus-log-control-api.h" #include "daemon-util.h" #include "homed-manager.h" +#include "homed-manager-bus.h" #include "log.h" #include "main-func.h" +#include "service-util.h" #include "signal-util.h" static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; int r; log_setup_service(); - umask(0022); + r = service_parse_argv("systemd-homed.service", + "A service to create, remove, change or inspect home areas.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); + umask(0022); if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.Home", 1) < 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set $SYSTEMD_BYPASS_USERDB: %m"); diff --git a/src/home/homed.conf b/src/home/homed.conf new file mode 100644 index 000000000..6de75fc5c --- /dev/null +++ b/src/home/homed.conf @@ -0,0 +1,16 @@ +# 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. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See homed.conf(5) for details + +[Home] +#DefaultStorage= +#DefaultFileSystemType=ext4 diff --git a/src/home/homework-cifs.c b/src/home/homework-cifs.c index 27f298144..cfceaed74 100644 --- a/src/home/homework-cifs.c +++ b/src/home/homework-cifs.c @@ -28,7 +28,7 @@ int home_prepare_cifs( char **pw; int r; - r = home_unshare_and_mount(NULL, NULL, false); + r = home_unshare_and_mount(NULL, NULL, false, user_record_mount_flags(h)); if (r < 0) return r; @@ -98,7 +98,7 @@ int home_prepare_cifs( int home_activate_cifs( UserRecord *h, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, UserRecord **ret_home) { _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; @@ -120,7 +120,7 @@ int home_activate_cifs( if (r < 0) return r; - r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home); + r = home_refresh(h, &setup, NULL, cache, NULL, &new_home); if (r < 0) return r; @@ -142,7 +142,8 @@ int home_create_cifs(UserRecord *h, UserRecord **ret_home) { _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; _cleanup_(closedirp) DIR *d = NULL; - int r, copy; + _cleanup_close_ int copy = -1; + int r; assert(h); assert(user_record_storage(h) == USER_CIFS); @@ -166,11 +167,9 @@ int home_create_cifs(UserRecord *h, UserRecord **ret_home) { if (copy < 0) return -errno; - d = fdopendir(copy); - if (!d) { - safe_close(copy); + d = take_fdopendir(©); + if (!d) return -errno; - } errno = 0; if (readdir_no_dot(d)) diff --git a/src/home/homework-cifs.h b/src/home/homework-cifs.h index 346be8826..ee799e2a4 100644 --- a/src/home/homework-cifs.h +++ b/src/home/homework-cifs.h @@ -6,6 +6,6 @@ int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup); -int home_activate_cifs(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home); +int home_activate_cifs(UserRecord *h, PasswordCache *cache, UserRecord **ret_home); int home_create_cifs(UserRecord *h, UserRecord **ret_home); diff --git a/src/home/homework-directory.c b/src/home/homework-directory.c index 8a4cb1732..7d00da214 100644 --- a/src/home/homework-directory.c +++ b/src/home/homework-directory.c @@ -26,7 +26,7 @@ int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *set int home_activate_directory( UserRecord *h, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, UserRecord **ret_home) { _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL; @@ -44,11 +44,11 @@ int home_activate_directory( assert_se(hdo = user_record_home_directory(h)); hd = strdupa(hdo); - r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home); + r = home_prepare(h, false, cache, &setup, &header_home); if (r < 0) return r; - r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home); + r = home_refresh(h, &setup, header_home, cache, NULL, &new_home); if (r < 0) return r; @@ -193,7 +193,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) { int home_resize_directory( UserRecord *h, bool already_activated, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home) { @@ -205,11 +205,11 @@ int home_resize_directory( assert(ret_home); assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)); - r = home_prepare(h, already_activated, pkcs11_decrypted_passwords, setup, NULL); + r = home_prepare(h, already_activated, cache, setup, NULL); if (r < 0) return r; - r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home); + r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home); if (r < 0) return r; diff --git a/src/home/homework-directory.h b/src/home/homework-directory.h index 047c3a70a..717837f34 100644 --- a/src/home/homework-directory.h +++ b/src/home/homework-directory.h @@ -5,6 +5,6 @@ #include "user-record.h" int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup); -int home_activate_directory(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home); +int home_activate_directory(UserRecord *h, PasswordCache *cache, UserRecord **ret_home); int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home); -int home_resize_directory(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home); +int home_resize_directory(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home); diff --git a/src/home/homework-fido2.c b/src/home/homework-fido2.c new file mode 100644 index 000000000..36fe059ab --- /dev/null +++ b/src/home/homework-fido2.c @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "hexdecoct.h" +#include "homework-fido2.h" +#include "strv.h" + +static int fido2_use_specific_token( + const char *path, + UserRecord *h, + UserRecord *secret, + const Fido2HmacSalt *salt, + char **ret) { + + _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL; + _cleanup_(fido_assert_free) fido_assert_t *a = NULL; + _cleanup_(fido_dev_free) fido_dev_t *d = NULL; + bool found_extension = false; + size_t n, hmac_size; + const void *hmac; + char **e; + int r; + + d = fido_dev_new(); + if (!d) + return log_oom(); + + r = fido_dev_open(d, path); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to open FIDO2 device %s: %s", path, fido_strerr(r)); + + if (!fido_dev_is_fido2(d)) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Specified device %s is not a FIDO2 device.", path); + + di = fido_cbor_info_new(); + if (!di) + return log_oom(); + + r = fido_dev_get_cbor_info(d, di); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get CBOR device info for %s: %s", path, fido_strerr(r)); + + e = fido_cbor_info_extensions_ptr(di); + n = fido_cbor_info_extensions_len(di); + + for (size_t i = 0; i < n; i++) + if (streq(e[i], "hmac-secret")) { + found_extension = true; + break; + } + + if (!found_extension) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path); + + a = fido_assert_new(); + if (!a) + return log_oom(); + + r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r)); + + r = fido_assert_set_hmac_salt(a, salt->salt, salt->salt_size); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r)); + + r = fido_assert_set_rp(a, "io.systemd.home"); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 assertion ID: %s", fido_strerr(r)); + + r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r)); + + r = fido_assert_allow_cred(a, salt->credential.id, salt->credential.size); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r)); + + r = fido_assert_set_up(a, h->fido2_user_presence_permitted <= 0 ? FIDO_OPT_FALSE : FIDO_OPT_TRUE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to set FIDO2 assertion user presence: %s", fido_strerr(r)); + + log_info("Asking FIDO2 token for authentication."); + + r = fido_dev_get_assert(d, a, NULL); /* try without pin first */ + if (r == FIDO_ERR_PIN_REQUIRED) { + char **i; + + /* OK, we needed a pin, try with all pins in turn */ + STRV_FOREACH(i, secret->token_pin) { + r = fido_dev_get_assert(d, a, *i); + if (r != FIDO_ERR_PIN_INVALID) + break; + } + } + + switch (r) { + case FIDO_OK: + break; + case FIDO_ERR_NO_CREDENTIALS: + return log_error_errno(SYNTHETIC_ERRNO(EBADSLT), + "Wrong security token; needed credentials not present on token."); + case FIDO_ERR_PIN_REQUIRED: + return log_error_errno(SYNTHETIC_ERRNO(ENOANO), + "Security token requires PIN."); + case FIDO_ERR_PIN_AUTH_BLOCKED: + return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD), + "PIN of security token is blocked, please remove/reinsert token."); + case FIDO_ERR_PIN_INVALID: + return log_error_errno(SYNTHETIC_ERRNO(ENOLCK), + "PIN of security token incorrect."); + case FIDO_ERR_UP_REQUIRED: + return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), + "User presence required."); + case FIDO_ERR_ACTION_TIMEOUT: + return log_error_errno(SYNTHETIC_ERRNO(ENOSTR), + "Token action timeout. (User didn't interact with token quickly enough.)"); + default: + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to ask token for assertion: %s", fido_strerr(r)); + } + + hmac = fido_assert_hmac_secret_ptr(a, 0); + if (!hmac) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret."); + + hmac_size = fido_assert_hmac_secret_len(a, 0); + + r = base64mem(hmac, hmac_size, ret); + if (r < 0) + return log_error_errno(r, "Failed to base64 encode HMAC secret: %m"); + + return 0; +} + +int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret) { + size_t allocated = 64, found = 0; + fido_dev_info_t *di = NULL; + int r; + + di = fido_dev_info_new(allocated); + if (!di) + return log_oom(); + + r = fido_dev_info_manifest(di, allocated, &found); + if (r == FIDO_ERR_INTERNAL) { + /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */ + r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices."); + goto finish; + } + if (r != FIDO_OK) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r)); + goto finish; + } + + for (size_t i = 0; i < found; i++) { + const fido_dev_info_t *entry; + const char *path; + + entry = fido_dev_info_ptr(di, i); + if (!entry) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get device information for FIDO device %zu.", i); + goto finish; + } + + path = fido_dev_info_path(entry); + if (!path) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to query FIDO device path."); + goto finish; + } + + r = fido2_use_specific_token(path, h, secret, salt, ret); + if (!IN_SET(r, + -EBADSLT, /* device doesn't understand our credential hash */ + -ENODEV /* device is not a FIDO2 device with HMAC-SECRET */)) + goto finish; + } + + r = -EAGAIN; + +finish: + fido_dev_info_free(&di, allocated); + return r; +} diff --git a/src/home/homework-fido2.h b/src/home/homework-fido2.h new file mode 100644 index 000000000..d3b142a92 --- /dev/null +++ b/src/home/homework-fido2.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "user-record.h" + +int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret); diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 696e26539..da9bb64b7 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -208,7 +208,7 @@ static int fscrypt_slot_try_many( } static int fscrypt_setup( - char **pkcs11_decrypted_passwords, + const PasswordCache *cache, char **password, HomeSetup *setup, void **ret_volume_key, @@ -230,6 +230,7 @@ static int fscrypt_setup( _cleanup_free_ char *value = NULL; size_t salt_size, encrypted_size; const char *nr, *e; + char **list; int n; /* Check if this xattr has the format 'trusted.fscrypt_slot' where '' is a 32bit unsigned integer */ @@ -256,19 +257,17 @@ static int fscrypt_setup( if (r < 0) return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa); - r = fscrypt_slot_try_many( - pkcs11_decrypted_passwords, - salt, salt_size, - encrypted, encrypted_size, - setup->fscrypt_key_descriptor, - ret_volume_key, ret_volume_key_size); - if (r == -ENOANO) + r = -ENOANO; + FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, password) { r = fscrypt_slot_try_many( - password, + list, salt, salt_size, encrypted, encrypted_size, setup->fscrypt_key_descriptor, ret_volume_key, ret_volume_key_size); + if (r != -ENOANO) + break; + } if (r < 0) { if (r != -ENOANO) return r; @@ -282,7 +281,7 @@ static int fscrypt_setup( int home_prepare_fscrypt( UserRecord *h, bool already_activated, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, HomeSetup *setup) { _cleanup_(erase_and_freep) void *volume_key = NULL; @@ -314,7 +313,7 @@ int home_prepare_fscrypt( memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); r = fscrypt_setup( - pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL, + cache, h->password, setup, &volume_key, @@ -584,7 +583,7 @@ int home_create_fscrypt( int home_passwd_fscrypt( UserRecord *h, HomeSetup *setup, - char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */ + PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */ char **effective_passwords /* new passwords */) { _cleanup_(erase_and_freep) void *volume_key = NULL; @@ -600,7 +599,7 @@ int home_passwd_fscrypt( assert(setup); r = fscrypt_setup( - pkcs11_decrypted_passwords, + cache, h->password, setup, &volume_key, diff --git a/src/home/homework-fscrypt.h b/src/home/homework-fscrypt.h index aa3bcd3a6..e5cf7baaa 100644 --- a/src/home/homework-fscrypt.h +++ b/src/home/homework-fscrypt.h @@ -4,7 +4,7 @@ #include "homework.h" #include "user-record.h" -int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup); +int home_prepare_fscrypt(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup); int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home); -int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords); +int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index d731d0d64..ba47688b0 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -8,6 +8,7 @@ #include "blkid-util.h" #include "blockdev-util.h" +#include "btrfs-util.h" #include "chattr-util.h" #include "dm-util.h" #include "errno-util.h" @@ -15,6 +16,7 @@ #include "fileio.h" #include "fs-util.h" #include "fsck-util.h" +#include "home-util.h" #include "homework-luks.h" #include "homework-mount.h" #include "id128-util.h" @@ -38,12 +40,6 @@ * strictly round disk sizes down to the next 1K boundary.*/ #define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023)) -static bool supported_fstype(const char *fstype) { - /* Limit the set of supported file systems a bit, as protection against little tested kernel file - * systems. Also, we only support the resize ioctls for these file systems. */ - return STR_IN_SET(fstype, "ext4", "btrfs", "xfs"); -} - static int probe_file_system_by_fd( int fd, char **ret_fstype, @@ -220,7 +216,7 @@ static int luks_setup( const char *cipher_mode, uint64_t volume_key_size, char **passwords, - char **pkcs11_decrypted_passwords, + const PasswordCache *cache, bool discard, struct crypt_device **ret, sd_id128_t *ret_found_uuid, @@ -231,6 +227,7 @@ static int luks_setup( _cleanup_(erase_and_freep) void *vk = NULL; sd_id128_t p; size_t vks; + char **list; int r; assert(node); @@ -282,12 +279,14 @@ static int luks_setup( if (!vk) return log_oom(); - r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks); - if (r == -ENOKEY) { - r = luks_try_passwords(cd, passwords, vk, &vks); - if (r == -ENOKEY) - return log_error_errno(r, "No valid password for LUKS superblock."); + r = -ENOKEY; + FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) { + r = luks_try_passwords(cd, list, vk, &vks); + if (r != -ENOKEY) + break; } + if (r == -ENOKEY) + return log_error_errno(r, "No valid password for LUKS superblock."); if (r < 0) return log_error_errno(r, "Failed to unlocks LUKS superblock: %m"); @@ -316,7 +315,7 @@ static int luks_setup( static int luks_open( const char *dm_name, char **passwords, - char **pkcs11_decrypted_passwords, + PasswordCache *cache, struct crypt_device **ret, sd_id128_t *ret_found_uuid, void **ret_volume_key, @@ -325,6 +324,7 @@ static int luks_open( _cleanup_(crypt_freep) struct crypt_device *cd = NULL; _cleanup_(erase_and_freep) void *vk = NULL; sd_id128_t p; + char **list; size_t vks; int r; @@ -365,12 +365,14 @@ static int luks_open( if (!vk) return log_oom(); - r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks); - if (r == -ENOKEY) { - r = luks_try_passwords(cd, passwords, vk, &vks); - if (r == -ENOKEY) - return log_error_errno(r, "No valid password for LUKS superblock."); + r = -ENOKEY; + FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) { + r = luks_try_passwords(cd, list, vk, &vks); + if (r != -ENOKEY) + break; } + if (r == -ENOKEY) + return log_error_errno(r, "No valid password for LUKS superblock."); if (r < 0) return log_error_errno(r, "Failed to unlocks LUKS superblock: %m"); @@ -626,7 +628,7 @@ static int luks_validate_home_record( struct crypt_device *cd, UserRecord *h, const void *volume_key, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, UserRecord **ret_luks_home_record) { int r, token; @@ -731,9 +733,10 @@ static int luks_validate_home_record( if (!user_record_compatible(h, lhr)) return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing."); - r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords); + r = user_record_authenticate(lhr, h, cache, /* strict_verify= */ true); if (r < 0) return r; + assert(r > 0); /* Insist that a password was verified */ *ret_luks_home_record = TAKE_PTR(lhr); return 0; @@ -892,19 +895,19 @@ int home_store_header_identity_luks( return 1; } -static int run_fitrim(int root_fd) { +int run_fitrim(int root_fd) { char buf[FORMAT_BYTES_MAX]; struct fstrim_range range = { .len = UINT64_MAX, }; /* If discarding is on, discard everything right after mounting, so that the discard setting takes - * effect on activation. */ + * effect on activation. (Also, optionally, trim on logout) */ assert(root_fd >= 0); if (ioctl(root_fd, FITRIM, &range) < 0) { - if (IN_SET(errno, ENOTTY, EOPNOTSUPP, EBADF)) { + if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EBADF) { log_debug_errno(errno, "File system does not support FITRIM, not trimming."); return 0; } @@ -917,15 +920,32 @@ static int run_fitrim(int root_fd) { return 1; } -static int run_fallocate(int backing_fd, const struct stat *st) { +int run_fitrim_by_path(const char *root_path) { + _cleanup_close_ int root_fd = -1; + + root_fd = open(root_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (root_fd < 0) + return log_error_errno(errno, "Failed to open file system '%s' for trimming: %m", root_path); + + return run_fitrim(root_fd); +} + +int run_fallocate(int backing_fd, const struct stat *st) { char buf[FORMAT_BYTES_MAX]; + struct stat stbuf; assert(backing_fd >= 0); - assert(st); /* If discarding is off, let's allocate the whole image before mounting, so that the setting takes * effect on activation */ + if (!st) { + if (fstat(backing_fd, &stbuf) < 0) + return log_error_errno(errno, "Failed to fstat(): %m"); + + st = &stbuf; + } + if (!S_ISREG(st->st_mode)) return 0; @@ -954,11 +974,21 @@ static int run_fallocate(int backing_fd, const struct stat *st) { return 1; } +int run_fallocate_by_path(const char *backing_path) { + _cleanup_close_ int backing_fd = -1; + + backing_fd = open(backing_path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (backing_fd < 0) + return log_error_errno(errno, "Failed to open '%s' for fallocate(): %m", backing_path); + + return run_fallocate(backing_fd, NULL); +} + int home_prepare_luks( UserRecord *h, bool already_activated, const char *force_image_path, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home) { @@ -986,7 +1016,7 @@ int home_prepare_luks( r = luks_open(setup->dm_name, h->password, - pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL, + cache, &cd, &found_luks_uuid, &volume_key, @@ -994,7 +1024,7 @@ int home_prepare_luks( if (r < 0) return r; - r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home); + r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home); if (r < 0) return r; @@ -1109,8 +1139,8 @@ int home_prepare_luks( h->luks_cipher_mode, h->luks_volume_key_size, h->password, - pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL, - user_record_luks_discard(h), + cache, + user_record_luks_discard(h) || user_record_luks_offline_discard(h), &cd, &found_luks_uuid, &volume_key, @@ -1120,7 +1150,7 @@ int home_prepare_luks( dm_activated = true; - r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home); + r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home); if (r < 0) goto fail; @@ -1132,7 +1162,7 @@ int home_prepare_luks( if (r < 0) goto fail; - r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h)); + r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h)); if (r < 0) goto fail; @@ -1146,6 +1176,9 @@ int home_prepare_luks( if (user_record_luks_discard(h)) (void) run_fitrim(root_fd); + + setup->image_fd = TAKE_FD(fd); + setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h)); } setup->loop = TAKE_PTR(loop); @@ -1191,7 +1224,7 @@ static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, stru int home_activate_luks( UserRecord *h, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, UserRecord **ret_home) { _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL; @@ -1223,7 +1256,7 @@ int home_activate_luks( h, false, NULL, - pkcs11_decrypted_passwords, + cache, &setup, &luks_home_record); if (r < 0) @@ -1241,7 +1274,7 @@ int home_activate_luks( h, &setup, luks_home_record, - pkcs11_decrypted_passwords, + cache, &sfs, &new_home); if (r < 0) @@ -1258,14 +1291,16 @@ int home_activate_luks( return r; setup.undo_mount = false; + setup.do_offline_fitrim = false; loop_device_relinquish(setup.loop); - r = dm_deferred_remove(setup.dm_name); + r = crypt_deactivate_by_name(NULL, setup.dm_name, CRYPT_DEACTIVATE_DEFERRED); if (r < 0) log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m"); setup.undo_dm = false; + setup.do_offline_fallocate = false; log_info("Everything completed."); @@ -1278,6 +1313,7 @@ int home_activate_luks( int home_deactivate_luks(UserRecord *h) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; _cleanup_free_ char *dm_name = NULL, *dm_node = NULL; + bool we_detached; int r; /* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we @@ -1292,23 +1328,45 @@ int home_deactivate_luks(UserRecord *h) { r = crypt_init_by_name(&cd, dm_name); if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) { - log_debug_errno(r, "LUKS device %s is already detached.", dm_name); - return false; + log_debug_errno(r, "LUKS device %s has already been detached.", dm_name); + we_detached = false; } else if (r < 0) return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name); + else { + log_info("Discovered used LUKS device %s.", dm_node); - log_info("Discovered used LUKS device %s.", dm_node); + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); - crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); + r = crypt_deactivate(cd, dm_name); + if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) { + log_debug_errno(r, "LUKS device %s is already detached.", dm_node); + we_detached = false; + } else if (r < 0) + return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node); + else { + log_info("LUKS device detaching completed."); + we_detached = true; + } + } - r = crypt_deactivate(cd, dm_name); - if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) - log_debug_errno(r, "LUKS device %s is already detached.", dm_node); - else if (r < 0) - return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node); + if (user_record_luks_offline_discard(h)) + log_debug("Not allocating on logout."); + else + (void) run_fallocate_by_path(user_record_image_path(h)); - log_info("LUKS device detaching completed."); - return true; + return we_detached; +} + +int home_trim_luks(UserRecord *h) { + assert(h); + + if (!user_record_luks_offline_discard(h)) { + log_debug("Not trimming on logout."); + return 0; + } + + (void) run_fitrim_by_path(user_record_home_directory(h)); + return 0; } static int run_mkfs( @@ -1412,7 +1470,7 @@ static int luks_format( const char *dm_name, sd_id128_t uuid, const char *label, - char **pkcs11_decrypted_passwords, + const PasswordCache *cache, char **effective_passwords, bool discard, UserRecord *hr, @@ -1481,7 +1539,8 @@ static int luks_format( STRV_FOREACH(pp, effective_passwords) { - if (strv_contains(pkcs11_decrypted_passwords, *pp)) { + if (strv_contains(cache->pkcs11_passwords, *pp) || + strv_contains(cache->fido2_passwords, *pp)) { log_debug("Using minimal PBKDF for slot %i", slot); r = crypt_set_pbkdf_type(cd, &minimal_pbkdf); } else { @@ -1666,7 +1725,7 @@ static int wait_for_devlink(const char *path) { usec_t until; int r; - /* let's wait for a device link to show up in /dev, with a time-out. This is good to do since we + /* let's wait for a device link to show up in /dev, with a timeout. This is good to do since we * return a /dev/disk/by-uuid/… link to our callers and they likely want to access it right-away, * hence let's wait until udev has caught up with our changes, and wait for the symlink to be * created. */ @@ -1765,9 +1824,48 @@ static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t * return 0; } +static int home_truncate( + UserRecord *h, + int fd, + const char *path, + uint64_t size) { + + bool trunc; + int r; + + assert(h); + assert(fd >= 0); + assert(path); + + trunc = user_record_luks_discard(h); + if (!trunc) { + r = fallocate(fd, 0, 0, size); + if (r < 0 && ERRNO_IS_NOT_SUPPORTED(errno)) { + /* Some file systems do not support fallocate(), let's gracefully degrade + * (ZFS, reiserfs, …) and fall back to truncation */ + log_notice_errno(errno, "Backing file system does not support fallocate(), falling back to ftruncate(), i.e. implicitly using non-discard mode."); + trunc = true; + } + } + + if (trunc) + r = ftruncate(fd, size); + + if (r < 0) { + if (ERRNO_IS_DISK_SPACE(errno)) { + log_error_errno(errno, "Not enough disk space to allocate home."); + return -ENOSPC; /* make recognizable */ + } + + return log_error_errno(errno, "Failed to truncate home image %s: %m", path); + } + + return 0; +} + int home_create_luks( UserRecord *h, - char **pkcs11_decrypted_passwords, + PasswordCache *cache, char **effective_passwords, UserRecord **ret_home) { @@ -1878,7 +1976,9 @@ int home_create_luks( if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0) return log_oom(); - if (user_record_luks_discard(h)) { + if (user_record_luks_discard(h) || user_record_luks_offline_discard(h)) { + /* If we want online or offline discard, discard once before we start using things. */ + if (ioctl(image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0) log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno, "Failed to issue full-device BLKDISCARD on device, ignoring: %m"); @@ -1917,20 +2017,9 @@ int home_create_luks( if (r < 0) log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", temporary_image_path); - if (user_record_luks_discard(h)) - r = ftruncate(image_fd, host_size); - else - r = fallocate(image_fd, 0, 0, host_size); - if (r < 0) { - if (ERRNO_IS_DISK_SPACE(errno)) { - log_debug_errno(errno, "Not enough disk space to allocate home."); - r = -ENOSPC; /* make recognizable */ - goto fail; - } - - r = log_error_errno(errno, "Failed to truncate home image %s: %m", temporary_image_path); + r = home_truncate(h, image_fd, temporary_image_path, host_size); + if (r < 0) goto fail; - } log_info("Allocating image file completed."); } @@ -1973,9 +2062,9 @@ int home_create_luks( dm_name, luks_uuid, user_record_user_name_and_realm(h), - pkcs11_decrypted_passwords, + cache, effective_passwords, - user_record_luks_discard(h), + user_record_luks_discard(h) || user_record_luks_offline_discard(h), h, &cd); if (r < 0) @@ -1997,7 +2086,7 @@ int home_create_luks( log_info("Formatting file system completed."); - r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h)); + r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h)); if (r < 0) goto fail; @@ -2009,8 +2098,10 @@ int home_create_luks( goto fail; } - if (mkdir(subdir, 0700) < 0) { - r = log_error_errno(errno, "Failed to create user directory in mounted image file: %m"); + /* Prefer using a btrfs subvolume if we can, fall back to directory otherwise */ + r = btrfs_subvol_make_fallback(subdir, 0700); + if (r < 0) { + log_error_errno(r, "Failed to create user directory in mounted image file: %m"); goto fail; } @@ -2053,6 +2144,12 @@ int home_create_luks( goto fail; } + if (user_record_luks_offline_discard(h)) { + r = run_fitrim(root_fd); + if (r < 0) + goto fail; + } + root_fd = safe_close(root_fd); r = umount_verbose("/run/systemd/user-home-mount"); @@ -2071,6 +2168,12 @@ int home_create_luks( loop = loop_device_unref(loop); + if (!user_record_luks_offline_discard(h)) { + r = run_fallocate(image_fd, NULL /* refresh stat() data */); + if (r < 0) + goto fail; + } + if (disk_uuid_path) (void) ioctl(image_fd, BLKRRPART, 0); @@ -2188,7 +2291,7 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) { return CAN_RESIZE_ONLINE; } -static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard) { +static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard, unsigned long flags) { _cleanup_free_ char *size_str = NULL; bool re_open = false, re_mount = false; pid_t resize_pid, fsck_pid; @@ -2258,7 +2361,7 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc /* Re-establish mounts and reopen the directory */ if (re_mount) { - r = home_mount_node(setup->dm_node, "ext4", discard); + r = home_mount_node(setup->dm_node, "ext4", discard, flags); if (r < 0) return r; @@ -2465,7 +2568,7 @@ static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_ta int home_resize_luks( UserRecord *h, bool already_activated, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home) { @@ -2551,11 +2654,11 @@ int home_resize_luks( } } - r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, setup, &header_home); + r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home); if (r < 0) return r; - r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home); + r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home); if (r < 0) return r; @@ -2625,19 +2728,9 @@ int home_resize_luks( if (S_ISREG(st.st_mode)) { /* Grow file size */ - - if (user_record_luks_discard(h)) - r = ftruncate(image_fd, new_image_size); - else - r = fallocate(image_fd, 0, 0, new_image_size); - if (r < 0) { - if (ERRNO_IS_DISK_SPACE(errno)) { - log_debug_errno(errno, "Not enough disk space to grow home."); - return -ENOSPC; /* make recognizable */ - } - - return log_error_errno(errno, "Failed to grow image file %s: %m", ip); - } + r = home_truncate(h, image_fd, ip, new_image_size); + if (r < 0) + return r; log_info("Growing of image file completed."); } @@ -2688,7 +2781,7 @@ int home_resize_luks( if (resize_type == CAN_RESIZE_ONLINE) r = resize_fs(setup->root_fd, new_fs_size, NULL); else - r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h)); + r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h)); if (r < 0) return log_error_errno(r, "Failed to resize file system: %m"); @@ -2769,13 +2862,14 @@ int home_resize_luks( int home_passwd_luks( UserRecord *h, HomeSetup *setup, - char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */ - char **effective_passwords /* new passwords */) { + PasswordCache *cache, /* the passwords acquired via PKCS#11/FIDO2 security tokens */ + char **effective_passwords /* new passwords */) { size_t volume_key_size, i, max_key_slots, n_effective; _cleanup_(erase_and_freep) void *volume_key = NULL; struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf; const char *type; + char **list; int r; assert(h); @@ -2800,12 +2894,14 @@ int home_passwd_luks( if (!volume_key) return log_oom(); - r = luks_try_passwords(setup->crypt_device, pkcs11_decrypted_passwords, volume_key, &volume_key_size); - if (r == -ENOKEY) { - r = luks_try_passwords(setup->crypt_device, h->password, volume_key, &volume_key_size); - if (r == -ENOKEY) - return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords."); + r = -ENOKEY; + FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) { + r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size); + if (r != -ENOKEY) + break; } + if (r == -ENOKEY) + return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords."); if (r < 0) return log_error_errno(r, "Failed to unlocks LUKS superblock: %m"); @@ -2825,7 +2921,8 @@ int home_passwd_luks( continue; } - if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) { + if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) || + strv_contains(cache->fido2_passwords, effective_passwords[i])) { log_debug("Using minimal PBKDF for slot %zu", i); r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf); } else { @@ -2922,9 +3019,10 @@ static int luks_try_resume( return -ENOKEY; } -int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) { +int home_unlock_luks(UserRecord *h, PasswordCache *cache) { _cleanup_free_ char *dm_name = NULL, *dm_node = NULL; _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + char **list; int r; assert(h); @@ -2940,12 +3038,14 @@ int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) { log_info("Discovered used LUKS device %s.", dm_node); crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); - r = luks_try_resume(cd, dm_name, pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL); - if (r == -ENOKEY) { - r = luks_try_resume(cd, dm_name, h->password); - if (r == -ENOKEY) - return log_error_errno(r, "No valid password for LUKS superblock."); + r = -ENOKEY; + FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) { + r = luks_try_resume(cd, dm_name, list); + if (r != -ENOKEY) + break; } + if (r == -ENOKEY) + return log_error_errno(r, "No valid password for LUKS superblock."); if (r < 0) return log_error_errno(r, "Failed to resume LUKS superblock: %m"); diff --git a/src/home/homework-luks.h b/src/home/homework-luks.h index 581255a22..b51f1ad7a 100644 --- a/src/home/homework-luks.h +++ b/src/home/homework-luks.h @@ -5,23 +5,24 @@ #include "homework.h" #include "user-record.h" -int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_luks_home); +int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home); -int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home); +int home_activate_luks(UserRecord *h, PasswordCache *cache, UserRecord **ret_home); int home_deactivate_luks(UserRecord *h); +int home_trim_luks(UserRecord *h); int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home); -int home_create_luks(UserRecord *h, char **pkcs11_decrypted_passwords, char **effective_passwords, UserRecord **ret_home); +int home_create_luks(UserRecord *h, PasswordCache *cache, char **effective_passwords, UserRecord **ret_home); int home_validate_update_luks(UserRecord *h, HomeSetup *setup); -int home_resize_luks(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home); +int home_resize_luks(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home); -int home_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords); +int home_passwd_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords); int home_lock_luks(UserRecord *h); -int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords); +int home_unlock_luks(UserRecord *h, PasswordCache *cache); static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) { int k; @@ -36,3 +37,8 @@ static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) { return (uint64_t) k; } + +int run_fitrim(int root_fd); +int run_fitrim_by_path(const char *root_path); +int run_fallocate(int backing_fd, const struct stat *st); +int run_fallocate_by_path(const char *backing_path); diff --git a/src/home/homework-mount.c b/src/home/homework-mount.c index 9e1116840..51c0a3864 100644 --- a/src/home/homework-mount.c +++ b/src/home/homework-mount.c @@ -20,7 +20,7 @@ static const char *mount_options_for_fstype(const char *fstype) { return NULL; } -int home_mount_node(const char *node, const char *fstype, bool discard) { +int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags) { _cleanup_free_ char *joined = NULL; const char *options, *discard_option; int r; @@ -38,7 +38,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) { } else options = discard_option; - r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, MS_NODEV|MS_NOSUID|MS_RELATIME, strempty(options)); + r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, flags|MS_RELATIME, strempty(options)); if (r < 0) return r; @@ -46,7 +46,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) { return 0; } -int home_unshare_and_mount(const char *node, const char *fstype, bool discard) { +int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags) { int r; if (unshare(CLONE_NEWNS) < 0) @@ -59,7 +59,7 @@ int home_unshare_and_mount(const char *node, const char *fstype, bool discard) { (void) mkdir_p("/run/systemd/user-home-mount", 0700); if (node) - return home_mount_node(node, fstype, discard); + return home_mount_node(node, fstype, discard, flags); return 0; } diff --git a/src/home/homework-mount.h b/src/home/homework-mount.h index d926756f7..cf7c8cfca 100644 --- a/src/home/homework-mount.h +++ b/src/home/homework-mount.h @@ -3,6 +3,6 @@ #include -int home_mount_node(const char *node, const char *fstype, bool discard); -int home_unshare_and_mount(const char *node, const char *fstype, bool discard); +int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags); +int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags); int home_move_mount(const char *user_name_and_realm, const char *target); diff --git a/src/home/homework-pkcs11.c b/src/home/homework-pkcs11.c index 915bc0e57..3a03fb720 100644 --- a/src/home/homework-pkcs11.c +++ b/src/home/homework-pkcs11.c @@ -62,10 +62,10 @@ int pkcs11_callback( goto decrypt; } - if (strv_isempty(data->secret->pkcs11_pin)) - return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security Token requires PIN."); + if (strv_isempty(data->secret->token_pin)) + return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security token requires PIN."); - STRV_FOREACH(i, data->secret->pkcs11_pin) { + STRV_FOREACH(i, data->secret->token_pin) { rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i)); if (rv == CKR_OK) { log_info("Successfully logged into security token '%s' with PIN.", token_label); diff --git a/src/home/homework.c b/src/home/homework.c index cd38d438b..83bd875d2 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -7,9 +7,11 @@ #include "copy.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "home-util.h" #include "homework-cifs.h" #include "homework-directory.h" +#include "homework-fido2.h" #include "homework-fscrypt.h" #include "homework-luks.h" #include "homework-mount.h" @@ -20,7 +22,6 @@ #include "missing_magic.h" #include "mount-util.h" #include "path-util.h" -#include "pkcs11-util.h" #include "rm-rf.h" #include "stat-util.h" #include "strv.h" @@ -31,14 +32,22 @@ /* Make sure a bad password always results in a 3s delay, no matter what */ #define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC) +void password_cache_free(PasswordCache *cache) { + if (!cache) + return; + + cache->pkcs11_passwords = strv_free_erase(cache->pkcs11_passwords); + cache->fido2_passwords = strv_free_erase(cache->fido2_passwords); +} + int user_record_authenticate( UserRecord *h, UserRecord *secret, - char ***pkcs11_decrypted_passwords) { + PasswordCache *cache, + bool strict_verify) { - bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, - pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false; - size_t n; + bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, need_user_presence_permitted = false, + pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false; int r; assert(h); @@ -46,14 +55,14 @@ int user_record_authenticate( /* Tries to authenticate a user record with the supplied secrets. i.e. checks whether at least one * supplied plaintext passwords matches a hashed password field of the user record. Or if a - * configured PKCS#11 token is around and can unlock the record. + * configured PKCS#11 or FIDO2 token is around and can unlock the record. * - * Note that the pkcs11_decrypted_passwords parameter is both an input and and output parameter: it - * is a list of configured, decrypted PKCS#11 passwords. We typically have to call this function - * multiple times over the course of an operation (think: on login we authenticate the host user - * record, the record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a - * list of passwords we already decrypted, so that we don't have to do the (slow an potentially - * interactive) PKCS#11 dance for the relevant token again and again. */ + * Note that the 'cache' parameter is both an input and output parameter: it contains lists of + * configured, decrypted PKCS#11/FIDO2 passwords. We typically have to call this function multiple + * times over the course of an operation (think: on login we authenticate the host user record, the + * record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a list of + * passwords we already decrypted, so that we don't have to do the (slow and potentially interactive) + * PKCS#11/FIDO2 dance for the relevant token again and again. */ /* First, let's see if the supplied plain-text passwords work? */ r = user_record_test_secret(h, secret); @@ -66,29 +75,48 @@ int user_record_authenticate( return log_error_errno(r, "Failed to validate password of record: %m"); else { log_info("Provided password unlocks user record."); - return 0; + return 1; } - /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */ - for (n = 0; n < h->n_pkcs11_encrypted_key; n++) { + /* Second, test cached PKCS#11 passwords */ + for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) { + char **pp; + + STRV_FOREACH(pp, cache->pkcs11_passwords) { + r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp); + if (r < 0) + return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m"); + if (r > 0) { + log_info("Previously acquired PKCS#11 password unlocks user record."); + return 1; + } + } + } + + /* Third, test cached FIDO2 passwords */ + for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) { + char **pp; + + /* 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); + if (r < 0) + return log_error_errno(r, "Failed to check supplied FIDO2 password: %m"); + if (r > 0) { + log_info("Previously acquired FIDO2 password unlocks user record."); + return 0; + } + } + } + + /* 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++) { #if HAVE_P11KIT _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = { .user_record = h, .secret = secret, .encrypted_key = h->pkcs11_encrypted_key + n, }; - char **pp; - - /* See if any of the previously calculated passwords work */ - STRV_FOREACH(pp, *pkcs11_decrypted_passwords) { - r = test_password_one(data.encrypted_key->hashed_password, *pp); - if (r < 0) - return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m"); - if (r > 0) { - log_info("Previously acquired PKCS#11 password unlocks user record."); - return 0; - } - } r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data); switch (r) { @@ -125,7 +153,7 @@ int user_record_authenticate( log_info("Decrypted password from PKCS#11 security token %s unlocks user record.", data.encrypted_key->uri); - r = strv_extend(pkcs11_decrypted_passwords, data.decrypted_password); + r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password); if (r < 0) return log_oom(); @@ -137,6 +165,55 @@ int user_record_authenticate( #endif } + /* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */ + for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) { +#if HAVE_LIBFIDO2 + _cleanup_(erase_and_freep) char *decrypted_password = NULL; + + r = fido2_use_token(h, secret, h->fido2_hmac_salt + n, &decrypted_password); + switch (r) { + case -EAGAIN: + need_token = true; + break; + case -ENOANO: + need_pin = true; + break; + case -EOWNERDEAD: + pin_locked = true; + break; + case -ENOLCK: + pin_incorrect = true; + break; + case -EMEDIUMTYPE: + need_user_presence_permitted = true; + break; + case -ENOSTR: + token_action_timeout = true; + break; + default: + if (r < 0) + return r; + + r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password); + if (r < 0) + return log_error_errno(r, "Failed to test FIDO2 password: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured FIDO2 security token does not decrypt encrypted key correctly."); + + log_info("Decrypted password from FIDO2 security token unlocks user record."); + + r = strv_extend(&cache->fido2_passwords, decrypted_password); + if (r < 0) + return log_oom(); + + return 1; + } +#else + need_token = true; + break; +#endif + } + /* Ordered by "relevance", i.e. the most "important" or "interesting" error condition is returned. */ if (pin_incorrect_one_try_left) return -EUCLEAN; @@ -146,8 +223,12 @@ int user_record_authenticate( return -ENOLCK; if (pin_locked) return -EOWNERDEAD; + if (token_action_timeout) + return -ENOSTR; if (need_protected_authentication_path_permitted) return -ERFKILL; + if (need_user_presence_permitted) + return -EMEDIUMTYPE; if (need_pin) return -ENOANO; if (need_token) @@ -155,8 +236,20 @@ int user_record_authenticate( if (need_password) return -ENOKEY; - /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */ - return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record."); + /* Hmm, this means neither PCKS#11/FIDO2 nor classic hashed passwords were supplied, we cannot + * authenticate this reasonably */ + if (strict_verify) + return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), + "No hashed passwords and no PKCS#11/FIDO2 tokens defined, cannot authenticate user record, refusing."); + + /* If strict verification is off this means we are possibly in the case where we encountered an + * unfixated record, i.e. a synthetic one that accordingly lacks any authentication data. In this + * case, allow the authentication to pass for now, so that the second (or third) authentication level + * (the ones of the user record in the LUKS header or inside the home directory) will then catch + * invalid passwords. The second/third authentication always runs in strict verification mode. */ + log_debug("No hashed passwords and no PKCS#11 tokens defined in record, cannot authenticate user record. " + "Deferring to embedded user record."); + return 0; } int home_setup_undo(HomeSetup *setup) { @@ -164,7 +257,15 @@ int home_setup_undo(HomeSetup *setup) { assert(setup); - setup->root_fd = safe_close(setup->root_fd); + if (setup->root_fd >= 0) { + if (setup->do_offline_fitrim) { + q = run_fitrim(setup->root_fd); + if (q < 0) + r = q; + } + + setup->root_fd = safe_close(setup->root_fd); + } if (setup->undo_mount) { q = umount_verbose("/run/systemd/user-home-mount"); @@ -178,8 +279,20 @@ int home_setup_undo(HomeSetup *setup) { r = q; } + if (setup->image_fd >= 0) { + if (setup->do_offline_fallocate) { + q = run_fallocate(setup->image_fd, NULL); + if (q < 0) + r = q; + } + + setup->image_fd = safe_close(setup->image_fd); + } + setup->undo_mount = false; setup->undo_dm = false; + setup->do_offline_fitrim = false; + setup->do_offline_fallocate = false; setup->dm_name = mfree(setup->dm_name); setup->dm_node = mfree(setup->dm_node); @@ -198,7 +311,7 @@ int home_setup_undo(HomeSetup *setup) { int home_prepare( UserRecord *h, bool already_activated, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home) { @@ -217,7 +330,7 @@ int home_prepare( switch (user_record_storage(h)) { case USER_LUKS: - return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home); + return home_prepare_luks(h, already_activated, NULL, cache, setup, ret_header_home); case USER_SUBVOLUME: case USER_DIRECTORY: @@ -225,7 +338,7 @@ int home_prepare( break; case USER_FSCRYPT: - r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup); + r = home_prepare_fscrypt(h, already_activated, cache, setup); break; case USER_CIFS: @@ -280,12 +393,10 @@ static int read_identity_file(int root_fd, JsonVariant **ret) { if (r < 0) return log_error_errno(r, "Embedded identity file is not a regular file, refusing: %m"); - identity_file = fdopen(identity_fd, "r"); + identity_file = take_fdopen(&identity_fd, "r"); if (!identity_file) return log_oom(); - identity_fd = -1; - r = json_parse_file(identity_file, ".identity", JSON_PARSE_SENSITIVE, ret, &line, &column); if (r < 0) return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column); @@ -319,14 +430,12 @@ static int write_identity_file(int root_fd, JsonVariant *v, uid_t uid) { if (identity_fd < 0) return log_error_errno(errno, "Failed to create .identity file in home directory: %m"); - identity_file = fdopen(identity_fd, "w"); + identity_file = take_fdopen(&identity_fd, "w"); if (!identity_file) { r = log_oom(); goto fail; } - identity_fd = -1; - json_variant_dump(normalized, JSON_FORMAT_PRETTY, identity_file, NULL); r = fflush_and_check(identity_file); @@ -359,7 +468,7 @@ int home_load_embedded_identity( int root_fd, UserRecord *header_home, UserReconcileMode mode, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, UserRecord **ret_embedded_home, UserRecord **ret_new_home) { @@ -386,9 +495,10 @@ int home_load_embedded_identity( return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing."); /* Insist that credentials the user supplies also unlocks any embedded records. */ - r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords); + r = user_record_authenticate(embedded_home, h, cache, /* strict_verify= */ true); if (r < 0) return r; + assert(r > 0); /* Insist that a password was verified */ /* At this point we have three records to deal with: * @@ -547,7 +657,7 @@ int home_refresh( UserRecord *h, HomeSetup *setup, UserRecord *header_home, - char ***pkcs11_decrypted_passwords, + PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home) { @@ -561,7 +671,7 @@ int home_refresh( /* When activating a home directory, does the identity work: loads the identity from the $HOME * directory, reconciles it with our idea, chown()s everything. */ - r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, pkcs11_decrypted_passwords, &embedded_home, &new_home); + r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, cache, &embedded_home, &new_home); if (r < 0) return r; @@ -586,7 +696,7 @@ int home_refresh( } static int home_activate(UserRecord *h, UserRecord **ret_home) { - _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL; + _cleanup_(password_cache_free) PasswordCache cache = {}; _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; int r; @@ -599,7 +709,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) { if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS)) return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h))); - r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); + r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false); if (r < 0) return r; @@ -618,7 +728,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) { switch (user_record_storage(h)) { case USER_LUKS: - r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home); + r = home_activate_luks(h, &cache, &new_home); if (r < 0) return r; @@ -627,14 +737,14 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) { case USER_SUBVOLUME: case USER_DIRECTORY: case USER_FSCRYPT: - r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home); + r = home_activate_directory(h, &cache, &new_home); if (r < 0) return r; break; case USER_CIFS: - r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home); + r = home_activate_cifs(h, &cache, &new_home); if (r < 0) return r; @@ -671,6 +781,12 @@ static int home_deactivate(UserRecord *h, bool force) { if (r < 0) return r; if (r == USER_TEST_MOUNTED) { + if (user_record_storage(h) == USER_LUKS) { + r = home_trim_luks(h); + if (r < 0) + return r; + } + if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0) return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h)); @@ -748,15 +864,16 @@ int home_populate(UserRecord *h, int dir_fd) { static int user_record_compile_effective_passwords( UserRecord *h, - char ***ret_effective_passwords, - char ***ret_pkcs11_decrypted_passwords) { + PasswordCache *cache, + char ***ret_effective_passwords) { - _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL; + _cleanup_(strv_free_erasep) char **effective = NULL; size_t n; char **i; int r; assert(h); + assert(cache); /* We insist on at least one classic hashed password to be defined in addition to any PKCS#11 one, as * a safe fallback, but also to simplify the password changing algorithm: there we require providing @@ -823,11 +940,37 @@ static int user_record_compile_effective_passwords( return log_oom(); } - if (ret_pkcs11_decrypted_passwords) { - r = strv_extend(&pkcs11_passwords, data.decrypted_password); + r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password); + if (r < 0) + return log_oom(); +#else + return -EBADSLT; +#endif + } + + for (n = 0; n < h->n_fido2_hmac_salt; n++) { +#if HAVE_LIBFIDO2 + _cleanup_(erase_and_freep) char *decrypted_password = NULL; + + r = fido2_use_token(h, h, h->fido2_hmac_salt + n, &decrypted_password); + if (r < 0) + return r; + + r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password); + if (r < 0) + return log_error_errno(r, "Failed to test FIDO2 password: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Decrypted password from token is not correct, refusing."); + + if (ret_effective_passwords) { + r = strv_extend(&effective, decrypted_password); if (r < 0) return log_oom(); } + + r = strv_extend(&cache->fido2_passwords, decrypted_password); + if (r < 0) + return log_oom(); #else return -EBADSLT; #endif @@ -835,15 +978,73 @@ static int user_record_compile_effective_passwords( if (ret_effective_passwords) *ret_effective_passwords = TAKE_PTR(effective); - if (ret_pkcs11_decrypted_passwords) - *ret_pkcs11_decrypted_passwords = TAKE_PTR(pkcs11_passwords); + + return 0; +} + +static int determine_default_storage(UserStorage *ret) { + UserStorage storage = _USER_STORAGE_INVALID; + const char *e; + int r; + + assert(ret); + + /* homed tells us via an environment variable which default storage to use */ + e = getenv("SYSTEMD_HOME_DEFAULT_STORAGE"); + if (e) { + storage = user_storage_from_string(e); + if (storage < 0) + log_warning("$SYSTEMD_HOME_DEFAULT_STORAGE set to invalid storage type, ignoring: %s", e); + else { + log_info("Using configured default storage '%s'.", user_storage_to_string(storage)); + *ret = storage; + return 0; + } + } + + /* When neither user nor admin specified the storage type to use, fix it to be LUKS — unless we run + * in a container where loopback devices and LUKS/DM are not available. Also, if /home is encrypted + * anyway, let's avoid duplicate encryption. Note that we typically default to the assumption of + * "classic" storage for most operations. However, if we create a new home, then let's user LUKS if + * nothing is specified. */ + + r = detect_container(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether we are in a container: %m"); + if (r == 0) { + r = path_is_encrypted("/home"); + if (r < 0) + log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m"); + if (r <= 0) { + log_info("Using automatic default storage of '%s'.", user_storage_to_string(USER_LUKS)); + *ret = USER_LUKS; + return 0; + } + + log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS)); + } else + log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS)); + + r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC); + if (r < 0) + log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m"); + if (r > 0) { + log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME)); + *ret = USER_SUBVOLUME; + } else { + log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY)); + *ret = USER_DIRECTORY; + } return 0; } static int home_create(UserRecord *h, UserRecord **ret_home) { - _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL; + _cleanup_(strv_free_erasep) char **effective_passwords = NULL; _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; + _cleanup_(password_cache_free) PasswordCache cache = {}; + UserStorage new_storage = _USER_STORAGE_INVALID; + const char *new_fs = NULL; int r; assert(h); @@ -853,7 +1054,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) { if (!uid_is_valid(h->uid)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing."); - r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords); + r = user_record_compile_effective_passwords(h, &cache, &effective_passwords); if (r < 0) return r; @@ -863,27 +1064,18 @@ static int home_create(UserRecord *h, UserRecord **ret_home) { if (r != USER_TEST_ABSENT) return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Home directory %s already exists, refusing.", user_record_home_directory(h)); - /* When the user didn't specify the storage type to use, fix it to be LUKS -- unless we run in a - * container where loopback devices and LUKS/DM are not available. Note that we typically default to - * the assumption of "classic" storage for most operations. However, if we create a new home, then - * let's user LUKS if nothing is specified. */ if (h->storage < 0) { - UserStorage new_storage; - - r = detect_container(); + r = determine_default_storage(&new_storage); if (r < 0) - return log_error_errno(r, "Failed to determine whether we are in a container: %m"); - if (r > 0) { - new_storage = USER_DIRECTORY; + return r; + } - r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC); - if (r < 0) - log_debug_errno(r, "Failed to determine file system of /home, ignoring: %m"); - - new_storage = r > 0 ? USER_SUBVOLUME : USER_DIRECTORY; - } else - new_storage = USER_LUKS; + if ((h->storage == USER_LUKS || + (h->storage < 0 && new_storage == USER_LUKS)) && + !h->file_system_type) + new_fs = getenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE"); + if (new_storage >= 0 || new_fs) { r = user_record_add_binding( h, new_storage, @@ -894,18 +1086,12 @@ static int home_create(UserRecord *h, UserRecord **ret_home) { NULL, NULL, UINT64_MAX, - NULL, + new_fs, NULL, UID_INVALID, GID_INVALID); if (r < 0) return log_error_errno(r, "Failed to change storage type to LUKS: %m"); - - if (!h->image_path_auto) { - h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), new_storage == USER_LUKS ? ".home" : ".homedir"); - if (!h->image_path_auto) - return log_oom(); - } } r = user_record_test_image_path_and_warn(h); @@ -917,7 +1103,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) { switch (user_record_storage(h)) { case USER_LUKS: - r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home); + r = home_create_luks(h, &cache, effective_passwords, &new_home); break; case USER_DIRECTORY: @@ -1043,10 +1229,9 @@ static int home_remove(UserRecord *h) { if (deleted) log_info("Everything completed."); - else { - log_notice("Nothing to remove."); - return -EALREADY; - } + else + return log_notice_errno(SYNTHETIC_ERRNO(EALREADY), + "Nothing to remove."); return 0; } @@ -1104,17 +1289,18 @@ static int home_validate_update(UserRecord *h, HomeSetup *setup) { static int home_update(UserRecord *h, UserRecord **ret) { _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL; - _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL; _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; + _cleanup_(password_cache_free) PasswordCache cache = {}; bool already_activated = false; int r; assert(h); assert(ret); - r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); + r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true); if (r < 0) return r; + assert(r > 0); /* Insist that a password was verified */ r = home_validate_update(h, &setup); if (r < 0) @@ -1122,11 +1308,11 @@ static int home_update(UserRecord *h, UserRecord **ret) { already_activated = r > 0; - r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home); + r = home_prepare(h, already_activated, &cache, &setup, &header_home); if (r < 0) return r; - r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &pkcs11_decrypted_passwords, &embedded_home, &new_home); + r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &cache, &embedded_home, &new_home); if (r < 0) return r; @@ -1158,7 +1344,7 @@ static int home_update(UserRecord *h, UserRecord **ret) { static int home_resize(UserRecord *h, UserRecord **ret) { _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; - _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL; + _cleanup_(password_cache_free) PasswordCache cache = {}; bool already_activated = false; int r; @@ -1168,9 +1354,10 @@ static int home_resize(UserRecord *h, UserRecord **ret) { if (h->disk_size == UINT64_MAX) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing."); - r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); + r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true); if (r < 0) return r; + assert(r > 0); /* Insist that a password was verified */ r = home_validate_update(h, &setup); if (r < 0) @@ -1181,12 +1368,12 @@ static int home_resize(UserRecord *h, UserRecord **ret) { switch (user_record_storage(h)) { case USER_LUKS: - return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret); + return home_resize_luks(h, already_activated, &cache, &setup, ret); case USER_DIRECTORY: case USER_SUBVOLUME: case USER_FSCRYPT: - return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret); + return home_resize_directory(h, already_activated, &cache, &setup, ret); default: return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h))); @@ -1195,8 +1382,9 @@ static int home_resize(UserRecord *h, UserRecord **ret) { static int home_passwd(UserRecord *h, UserRecord **ret_home) { _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL; - _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL; + _cleanup_(strv_free_erasep) char **effective_passwords = NULL; _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; + _cleanup_(password_cache_free) PasswordCache cache = {}; bool already_activated = false; int r; @@ -1206,7 +1394,7 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) { if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Changing password of home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h))); - r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords); + r = user_record_compile_effective_passwords(h, &cache, &effective_passwords); if (r < 0) return r; @@ -1216,24 +1404,24 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) { already_activated = r > 0; - r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home); + r = home_prepare(h, already_activated, &cache, &setup, &header_home); if (r < 0) return r; - r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &pkcs11_decrypted_passwords, &embedded_home, &new_home); + r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &cache, &embedded_home, &new_home); if (r < 0) return r; switch (user_record_storage(h)) { case USER_LUKS: - r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords); + r = home_passwd_luks(h, &setup, &cache, effective_passwords); if (r < 0) return r; break; case USER_FSCRYPT: - r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords); + r = home_passwd_fscrypt(h, &setup, &cache, effective_passwords); if (r < 0) return r; break; @@ -1271,14 +1459,14 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) { static int home_inspect(UserRecord *h, UserRecord **ret_home) { _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL; _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT; - _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL; + _cleanup_(password_cache_free) PasswordCache cache = {}; bool already_activated = false; int r; assert(h); assert(ret_home); - r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); + r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false); if (r < 0) return r; @@ -1288,11 +1476,11 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) { already_activated = r > 0; - r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home); + r = home_prepare(h, already_activated, &cache, &setup, &header_home); if (r < 0) return r; - r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &pkcs11_decrypted_passwords, NULL, &new_home); + r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &cache, NULL, &new_home); if (r < 0) return r; @@ -1335,7 +1523,7 @@ static int home_lock(UserRecord *h) { } static int home_unlock(UserRecord *h) { - _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL; + _cleanup_(password_cache_free) PasswordCache cache = {}; int r; assert(h); @@ -1348,11 +1536,11 @@ static int home_unlock(UserRecord *h) { /* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on * that mount until we have resumed the device. */ - r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords); + r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false); if (r < 0) return r; - r = home_unlock_luks(h, &pkcs11_decrypted_passwords); + r = home_unlock_luks(h, &cache); if (r < 0) return r; @@ -1406,8 +1594,8 @@ static int run(int argc, char *argv[]) { /* Well known return values of these operations, that systemd-homed knows and converts to proper D-Bus errors: * - * EMSGSIZE → file systems of this type cannnot be shrinked - * ETXTBSY → file systems of this type can only be shrinked offline + * EMSGSIZE → file systems of this type cannot be shrunk + * ETXTBSY → file systems of this type can only be shrunk offline * ERANGE → file system size too small * ENOLINK → system does not support selected storage backend * EPROTONOSUPPORT → system does not support selected file system @@ -1415,15 +1603,18 @@ static int run(int argc, char *argv[]) { * ESOCKTNOSUPPORT → operation not support on this file system * ENOKEY → password incorrect (or not sufficient, or not supplied) * EBADSLT → similar, but PKCS#11 device is defined and might be able to provide password, if it was plugged in which it is not - * ENOANO → suitable PKCS#11 device found, but PIN is missing to unlock it + * ENOANO → suitable PKCS#11/FIDO2 device found, but PIN is missing to unlock it * ERFKILL → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given - * EOWNERDEAD → suitable PKCS#11 device found, but its PIN is locked - * ENOLCK → suitable PKCS#11 device found, but PIN incorrect + * EMEDIUMTYPE → suitable FIDO2 device found, but OK to ask for user presence not given + * ENOSTR → suitable FIDO2 device found, but user didn't react to action request on token quickly enough + * EOWNERDEAD → suitable PKCS#11/FIDO2 device found, but its PIN is locked + * ENOLCK → suitable PKCS#11/FIDO2 device found, but PIN incorrect * ETOOMANYREFS → suitable PKCS#11 device found, but PIN incorrect, and only few tries left * EUCLEAN → suitable PKCS#11 device found, but PIN incorrect, and only one try left * EBUSY → file system is currently active * ENOEXEC → file system is currently not active * ENOSPC → not enough disk space for operation + * EKEYREVOKED → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate */ if (streq(argv[1], "activate")) diff --git a/src/home/homework.h b/src/home/homework.h index 81698b760..ce8f2a461 100644 --- a/src/home/homework.h +++ b/src/home/homework.h @@ -17,6 +17,7 @@ typedef struct HomeSetup { LoopDevice *loop; struct crypt_device *crypt_device; int root_fd; + int image_fd; sd_id128_t found_partition_uuid; sd_id128_t found_luks_uuid; sd_id128_t found_fs_uuid; @@ -28,30 +29,41 @@ typedef struct HomeSetup { bool undo_dm; bool undo_mount; + bool do_offline_fitrim; + bool do_offline_fallocate; uint64_t partition_offset; uint64_t partition_size; } HomeSetup; +typedef struct PasswordCache { + /* Decoding passwords from security tokens is expensive and typically requires user interaction, hence cache any we already figured out. */ + char **pkcs11_passwords; + char **fido2_passwords; +} PasswordCache; + +void password_cache_free(PasswordCache *cache); + #define HOME_SETUP_INIT \ { \ .root_fd = -1, \ + .image_fd = -1, \ .partition_offset = UINT64_MAX, \ .partition_size = UINT64_MAX, \ } int home_setup_undo(HomeSetup *setup); -int home_prepare(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_header_home); +int home_prepare(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home); -int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, struct statfs *ret_statfs, UserRecord **ret_new_home); +int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home); int home_populate(UserRecord *h, int dir_fd); -int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, char ***pkcs11_decrypted_passwords, UserRecord **ret_embedded_home, UserRecord **ret_new_home); +int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, PasswordCache *cache, UserRecord **ret_embedded_home, UserRecord **ret_new_home); int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home); int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup); -int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords); +int user_record_authenticate(UserRecord *h, UserRecord *secret, PasswordCache *cache, bool strict_verify); int home_sync_and_statfs(int root_fd, struct statfs *ret); diff --git a/src/home/meson.build b/src/home/meson.build index eb6da0b69..797f3a3c6 100644 --- a/src/home/meson.build +++ b/src/home/meson.build @@ -14,6 +14,7 @@ systemd_homework_sources = files(''' homework-mount.c homework-mount.h homework-pkcs11.h + homework-fido2.h homework-quota.c homework-quota.h homework.c @@ -25,12 +26,17 @@ systemd_homework_sources = files(''' if conf.get('HAVE_P11KIT') == 1 systemd_homework_sources += files('homework-pkcs11.c') endif +if conf.get('HAVE_LIBFIDO2') == 1 + systemd_homework_sources += files('homework-fido2.c') +endif systemd_homed_sources = files(''' home-util.c home-util.h homed-bus.c homed-bus.h + homed-conf.c + homed-conf.h homed-home-bus.c homed-home-bus.h homed-home.c @@ -52,9 +58,21 @@ systemd_homed_sources = files(''' user-record-util.h '''.split()) +homed_gperf_c = custom_target( + 'homed_gperf.c', + input : 'homed-gperf.gperf', + output : 'homed-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_homed_sources += [homed_gperf_c] + homectl_sources = files(''' home-util.c home-util.h + homectl-fido2.c + homectl-fido2.h + homectl-pkcs11.c + homectl-pkcs11.h homectl.c pwquality-util.c pwquality-util.h @@ -78,4 +96,7 @@ if conf.get('ENABLE_HOMED') == 1 install_dir : dbussystemservicedir) install_data('org.freedesktop.home1.policy', install_dir : polkitpolicydir) + + install_data('homed.conf', + install_dir : pkgsysconfdir) endif diff --git a/src/home/pam_systemd_home.c b/src/home/pam_systemd_home.c index 5a3c817a5..2c2c7a081 100644 --- a/src/home/pam_systemd_home.c +++ b/src/home/pam_systemd_home.c @@ -6,6 +6,7 @@ #include "sd-bus.h" #include "bus-common-errors.h" +#include "bus-locator.h" #include "errno-util.h" #include "fd-util.h" #include "home-util.h" @@ -17,11 +18,6 @@ #include "user-record.h" #include "user-util.h" -/* Used for the "systemd-user-record-is-homed" PAM data field, to indicate whether we know whether this user - * record is managed by homed or by something else. */ -#define USER_RECORD_IS_HOMED INT_TO_PTR(1) -#define USER_RECORD_IS_OTHER INT_TO_PTR(2) - static int parse_argv( pam_handle_t *handle, int argc, const char **argv, @@ -64,29 +60,61 @@ static int parse_argv( return 0; } +static int parse_env( + pam_handle_t *handle, + bool *please_suspend) { + + const char *v; + int r; + + /* Let's read the suspend setting from an env var in addition to the PAM command line. That makes it + * easy to declare the features of a display manager in code rather than configuration, and this is + * really a feature of code */ + + v = pam_getenv(handle, "SYSTEMD_HOME_SUSPEND"); + if (!v) { + /* Also check the process env block, so that people can control this via an env var from the + * outside of our process. */ + v = secure_getenv("SYSTEMD_HOME_SUSPEND"); + if (!v) + return 0; + } + + r = parse_boolean(v); + if (r < 0) + pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v); + else if (please_suspend) + *please_suspend = r; + + return 0; +} + static int acquire_user_record( pam_handle_t *handle, + const char *username, UserRecord **ret_record) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *ur = NULL; _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - const char *username = NULL, *json = NULL; - const void *b = NULL; + _cleanup_free_ char *homed_field = NULL; + const char *json = NULL; int r; assert(handle); - r = pam_get_user(handle, &username, NULL); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r)); - return r; - } + if (!username) { + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r)); + return r; + } - if (isempty(username)) { - pam_syslog(handle, LOG_ERR, "User name not set."); - return PAM_SERVICE_ERR; + if (isempty(username)) { + pam_syslog(handle, LOG_ERR, "User name not set."); + return PAM_SERVICE_ERR; + } } /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for @@ -94,46 +122,36 @@ static int acquire_user_record( if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0)) return PAM_USER_UNKNOWN; - /* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */ - r = pam_get_data(handle, "systemd-user-record-is-homed", &b); - if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { - /* Failure */ - pam_syslog(handle, LOG_ERR, "Failed to get PAM user-record-is-homed flag: %s", pam_strerror(handle, r)); - return r; - } else if (b == NULL) - /* Nothing cached yet, need to acquire fresh */ - json = NULL; - else if (b != USER_RECORD_IS_HOMED) - /* Definitely not a homed record */ - return PAM_USER_UNKNOWN; - else { - /* It's a homed record, let's use the cache, so that we can share it between the session and - * the authentication hooks */ - r = pam_get_data(handle, "systemd-user-record", (const void**) &json); - if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { - pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r)); - return r; - } - } + /* We cache the user record in the PAM context. We use a field name that includes the username, since + * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo' + * creates a single PAM context and first authenticates it with the user set to the originating user, + * then updates the user for the destination user and issues the session stack with the same PAM + * context. We thus must be prepared that the user record changes between calls and we keep any + * caching separate. */ + homed_field = strjoin("systemd-home-user-record-", username); + if (!homed_field) + return pam_log_oom(handle); - if (!json) { + /* Let's use the cache, so that we can share it between the session and the authentication hooks */ + r = pam_get_data(handle, homed_field, (const void**) &json); + if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { + pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r)); + return r; + } + if (r == PAM_SUCCESS && json) { + /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as + * negative cache indicator) */ + if (json == (void*) -1) + return PAM_USER_UNKNOWN; + } else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *json_copy = NULL; + _cleanup_free_ char *generic_field = NULL, *json_copy = NULL; r = pam_acquire_bus_connection(handle, &bus); if (r != PAM_SUCCESS) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "GetUserRecordByName", - &error, - &reply, - "s", - username); + r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username); if (r < 0) { if (sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN) || sd_bus_error_has_name(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) { @@ -154,23 +172,38 @@ static int acquire_user_record( if (r < 0) return pam_bus_log_parse_error(handle, r); + /* First copy: for the homed-specific data field, i.e. where we know the user record is from + * homed */ json_copy = strdup(json); if (!json_copy) return pam_log_oom(handle); - r = pam_set_data(handle, "systemd-user-record", json_copy, pam_cleanup_free); + r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free); if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r)); + pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s", + homed_field, pam_strerror(handle, r)); + return r; + } + + /* Take a second copy: for the generic data field, the one which we share with + * pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed + * and non-homed user records. */ + json_copy = strdup(json); + if (!json_copy) + return pam_log_oom(handle); + + generic_field = strjoin("systemd-user-record-", username); + if (!generic_field) + return pam_log_oom(handle); + + r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s", + homed_field, pam_strerror(handle, r)); return r; } TAKE_PTR(json_copy); - - r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r)); - return r; - } } r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL); @@ -189,6 +222,7 @@ static int acquire_user_record( return PAM_SERVICE_ERR; } + /* Safety check if cached record actually matches what we are looking for */ if (!streq_ptr(username, ur->user_name)) { pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name."); return PAM_SERVICE_ERR; @@ -201,23 +235,36 @@ static int acquire_user_record( user_unknown: /* Cache this, so that we don't check again */ - r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_OTHER, NULL); + r = pam_set_data(handle, homed_field, (void*) -1, NULL); if (r != PAM_SUCCESS) - pam_syslog(handle, LOG_ERR, "Failed to set PAM user-record-is-homed flag, ignoring: %s", pam_strerror(handle, r)); + pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s' to invalid, ignoring: %s", + homed_field, pam_strerror(handle, r)); return PAM_USER_UNKNOWN; } -static int release_user_record(pam_handle_t *handle) { +static int release_user_record(pam_handle_t *handle, const char *username) { + _cleanup_free_ char *homed_field = NULL, *generic_field = NULL; int r, k; - r = pam_set_data(handle, "systemd-user-record", NULL, NULL); - if (r != PAM_SUCCESS) - pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r)); + assert(handle); + assert(username); - k = pam_set_data(handle, "systemd-user-record-is-homed", NULL, NULL); + homed_field = strjoin("systemd-home-user-record-", username); + if (!homed_field) + return pam_log_oom(handle); + + r = pam_set_data(handle, homed_field, NULL, NULL); + if (r != PAM_SUCCESS) + pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", homed_field, pam_strerror(handle, r)); + + generic_field = strjoin("systemd-user-record-", username); + if (!generic_field) + return pam_log_oom(handle); + + k = pam_set_data(handle, generic_field, NULL, NULL); if (k != PAM_SUCCESS) - pam_syslog(handle, LOG_ERR, "Failed to release PAM user-record-is-homed flag: %s", pam_strerror(handle, k)); + pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", generic_field, pam_strerror(handle, k)); return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r; } @@ -312,7 +359,7 @@ static int handle_generic_user_record_error( return PAM_AUTHTOK_ERR; } - r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false); + r = user_record_set_token_pin(secret, STRV_MAKE(newp), false); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r)); return PAM_SERVICE_ERR; @@ -328,6 +375,21 @@ static int handle_generic_user_record_error( return PAM_SERVICE_ERR; } + } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) { + + (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name); + + r = user_record_set_fido2_user_presence_permitted(secret, true); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r)); + return PAM_SERVICE_ERR; + } + + } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) { + + (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"); + return PAM_SERVICE_ERR; + } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) { _cleanup_(erase_and_freep) char *newp = NULL; @@ -341,7 +403,7 @@ static int handle_generic_user_record_error( return PAM_AUTHTOK_ERR; } - r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false); + r = user_record_set_token_pin(secret, STRV_MAKE(newp), false); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r)); return PAM_SERVICE_ERR; @@ -360,7 +422,7 @@ static int handle_generic_user_record_error( return PAM_AUTHTOK_ERR; } - r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false); + r = user_record_set_token_pin(secret, STRV_MAKE(newp), false); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r)); return PAM_SERVICE_ERR; @@ -379,7 +441,7 @@ static int handle_generic_user_record_error( return PAM_AUTHTOK_ERR; } - r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false); + r = user_record_set_token_pin(secret, STRV_MAKE(newp), false); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r)); return PAM_SERVICE_ERR; @@ -403,7 +465,9 @@ static int acquire_home( bool do_auth = please_authenticate, home_not_active = false, home_locked = false; _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; _cleanup_close_ int acquired_fd = -1; + _cleanup_free_ char *fd_field = NULL; const void *home_fd_ptr = NULL; + const char *username = NULL; unsigned n_attempts = 0; int r; @@ -417,8 +481,27 @@ static int acquire_home( * authenticates, while the other PAM hooks unset it so that they can a ref of their own without * authentication if possible, but with authentication if necessary. */ + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r)); + return r; + } + + if (isempty(username)) { + pam_syslog(handle, LOG_ERR, "User name not set."); + return PAM_SERVICE_ERR; + } + /* If we already have acquired the fd, let's shortcut this */ - r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr); + fd_field = strjoin("systemd-home-fd-", username); + if (!fd_field) + return pam_log_oom(handle); + + r = pam_get_data(handle, fd_field, &home_fd_ptr); + if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { + pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r)); + return r; + } if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0) return PAM_SUCCESS; @@ -426,12 +509,12 @@ static int acquire_home( if (r != PAM_SUCCESS) return r; - r = acquire_user_record(handle, &ur); + r = acquire_user_record(handle, username, &ur); if (r != PAM_SUCCESS) return r; /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it - * might happen that the the record we stored on the host does not match the encryption password of + * might happen that the record we stored on the host does not match the encryption password of * the LUKS image in case the image was used in a different system where the password was * changed. In that case it will happen that the LUKS password and the host password are * different, and we handle that by collecting and passing multiple passwords in that case. Hence we @@ -467,13 +550,7 @@ static int acquire_home( } } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - do_auth ? "AcquireHome" : "RefHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, do_auth ? "AcquireHome" : "RefHome"); if (r < 0) return pam_bus_log_create_error(handle, r); @@ -548,7 +625,7 @@ static int acquire_home( do_auth = true; } - r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd); + r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r)); return r; @@ -559,7 +636,7 @@ static int acquire_home( /* We likely just activated the home directory, let's flush out the user record, since a * newer embedded user record might have been acquired from the activation. */ - r = release_user_record(handle); + r = release_user_record(handle, ur->user_name); if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) return r; } @@ -569,15 +646,27 @@ static int acquire_home( return PAM_SUCCESS; } -static int release_home_fd(pam_handle_t *handle) { +static int release_home_fd(pam_handle_t *handle, const char *username) { + _cleanup_free_ char *fd_field = NULL; const void *home_fd_ptr = NULL; int r; - r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr); - if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0) - return PAM_NO_MODULE_DATA; + assert(handle); + assert(username); - r = pam_set_data(handle, "systemd-home-fd", NULL, NULL); + fd_field = strjoin("systemd-home-fd-", username); + if (!fd_field) + return pam_log_oom(handle); + + r = pam_get_data(handle, fd_field, &home_fd_ptr); + if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0)) + return PAM_NO_MODULE_DATA; + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r)); + return r; + } + + r = pam_set_data(handle, fd_field, NULL, NULL); if (r != PAM_SUCCESS) pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r)); @@ -591,6 +680,9 @@ _public_ PAM_EXTERN int pam_sm_authenticate( bool debug = false, suspend_please = false; + if (parse_env(handle, &suspend_please) < 0) + return PAM_AUTH_ERR; + if (parse_argv(handle, argc, argv, &suspend_please, @@ -615,6 +707,9 @@ _public_ PAM_EXTERN int pam_sm_open_session( bool debug = false, suspend_please = false; int r; + if (parse_env(handle, &suspend_please) < 0) + return PAM_SESSION_ERR; + if (parse_argv(handle, argc, argv, &suspend_please, @@ -636,6 +731,12 @@ _public_ PAM_EXTERN int pam_sm_open_session( return r; } + r = pam_putenv(handle, suspend_please ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0"); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: %s", pam_strerror(handle, r)); + return r; + } + /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are * not going to process the bus connection in that time, so let's better close before the daemon * kicks us off because we are not processing anything. */ @@ -664,31 +765,30 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (debug) pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end"); - /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome() - * call will be able to do its thing. */ - r = release_home_fd(handle); - if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */ - return PAM_SUCCESS; - if (r != PAM_SUCCESS) - return r; - r = pam_get_user(handle, &username, NULL); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r)); return r; } + if (isempty(username)) { + pam_syslog(handle, LOG_ERR, "User name not set."); + return PAM_SERVICE_ERR; + } + + /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome() + * call will be able to do its thing. */ + r = release_home_fd(handle, username); + if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */ + return PAM_SUCCESS; + if (r != PAM_SUCCESS) + return r; + r = pam_acquire_bus_connection(handle, &bus); if (r != PAM_SUCCESS) return r; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ReleaseHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome"); if (r < 0) return pam_bus_log_create_error(handle, r); @@ -720,6 +820,9 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt( usec_t t; int r; + if (parse_env(handle, &please_suspend) < 0) + return PAM_AUTH_ERR; + if (parse_argv(handle, argc, argv, &please_suspend, @@ -735,7 +838,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt( if (r != PAM_SUCCESS) return r; - r = acquire_user_record(handle, &ur); + r = acquire_user_record(handle, NULL, &ur); if (r != PAM_SUCCESS) return r; @@ -843,7 +946,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok( if (r != PAM_SUCCESS) return r; - r = acquire_user_record(handle, &ur); + r = acquire_user_record(handle, NULL, &ur); if (r != PAM_SUCCESS) return r; @@ -888,7 +991,6 @@ _public_ PAM_EXTERN int pam_sm_chauthtok( if (FLAGS_SET(flags, PAM_PRELIM_CHECK)) return PAM_SUCCESS; - old_secret = user_record_new(); if (!old_secret) return pam_log_oom(handle); @@ -915,13 +1017,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.home1", - "/org/freedesktop/home1", - "org.freedesktop.home1.Manager", - "ChangePasswordHome"); + r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome"); if (r < 0) return pam_bus_log_create_error(handle, r); diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c index 34f9d76cb..5d0ac8653 100644 --- a/src/home/user-record-util.c +++ b/src/home/user-record-util.c @@ -172,7 +172,7 @@ int user_record_reconcile( * -REMCHG: identity records are not about the same user * -ESTALE: embedded identity record is equally new or newer than supplied record * - * Return the new record to use, which is either the the embedded record updated with the host + * Return the new record to use, which is either the embedded record updated with the host * binding or the host record. In both cases the secret data is stripped. */ assert(host); @@ -276,7 +276,7 @@ int user_record_add_binding( _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL; char smid[SD_ID128_STRING_MAX], partition_uuids[37], luks_uuids[37], fs_uuids[37]; - _cleanup_free_ char *ip = NULL, *hd = NULL; + _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL; sd_id128_t mid; int r; @@ -294,6 +294,10 @@ int user_record_add_binding( ip = strdup(image_path); if (!ip) return -ENOMEM; + } else if (!h->image_path && storage >= 0) { + r = user_record_build_image_path(storage, user_record_user_name_and_realm(h), &ip_auto); + if (r < 0) + return r; } if (home_directory) { @@ -302,6 +306,24 @@ int user_record_add_binding( return -ENOMEM; } + if (file_system_type) { + fst = strdup(file_system_type); + if (!fst) + return -ENOMEM; + } + + if (luks_cipher) { + lc = strdup(luks_cipher); + if (!lc) + return -ENOMEM; + } + + if (luks_cipher_mode) { + lcm = strdup(luks_cipher_mode); + if (!lcm) + return -ENOMEM; + } + r = json_build(&new_binding_entry, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)), @@ -348,6 +370,8 @@ int user_record_add_binding( if (ip) free_and_replace(h->image_path, ip); + if (ip_auto) + free_and_replace(h->image_path_auto, ip_auto); if (!sd_id128_is_null(partition_uuid)) h->partition_uuid = partition_uuid; @@ -358,11 +382,22 @@ int user_record_add_binding( if (!sd_id128_is_null(fs_uuid)) h->file_system_uuid = fs_uuid; + if (lc) + free_and_replace(h->luks_cipher, lc); + if (lcm) + free_and_replace(h->luks_cipher_mode, lcm); + if (luks_volume_key_size != UINT64_MAX) + h->luks_volume_key_size = luks_volume_key_size; + + if (fst) + free_and_replace(h->file_system_type, fst); if (hd) free_and_replace(h->home_directory, hd); if (uid_is_valid(uid)) h->uid = uid; + if (gid_is_valid(gid)) + h->gid = gid; h->mask |= USER_RECORD_BINDING; return 1; @@ -460,7 +495,7 @@ int user_record_test_image_path(UserRecord *h) { if (S_ISBLK(st.st_mode)) { /* For block devices we can't really be sure if the device referenced actually is the * fs we look for or some other file system (think: what does /dev/sdb1 refer - * to?). Hence, let's return USER_TEST_MAYBE as an ambigious return value for these + * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these * case, except if the device path used is one of the paths that is based on a * filesystem or partition UUID or label, because in those cases we can be sure we * are referring to the right device. */ @@ -840,6 +875,8 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) { if (r < 0) return r; + json_variant_sensitive(w); + r = json_variant_set_field(&h->json, "secret", w); if (r < 0) return r; @@ -850,7 +887,7 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) { return 0; } -int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) { +int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) { _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; _cleanup_(strv_free_erasep) char **e = NULL; int r; @@ -862,17 +899,17 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) { if (!e) return -ENOMEM; - r = strv_extend_strv(&e, h->pkcs11_pin, true); + r = strv_extend_strv(&e, h->token_pin, true); if (r < 0) return r; strv_uniq(e); - if (strv_equal(h->pkcs11_pin, e)) + if (strv_equal(h->token_pin, e)) return 0; } else { - if (strv_equal(h->pkcs11_pin, pin)) + if (strv_equal(h->token_pin, pin)) return 0; e = strv_copy(pin); @@ -885,7 +922,7 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) { w = json_variant_ref(json_variant_by_key(h->json, "secret")); if (strv_isempty(e)) - r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin")); + r = json_variant_filter(&w, STRV_MAKE("tokenPin")); else { _cleanup_(json_variant_unrefp) JsonVariant *l = NULL; @@ -895,16 +932,18 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) { json_variant_sensitive(l); - r = json_variant_set_field(&w, "pkcs11Pin", l); + r = json_variant_set_field(&w, "tokenPin", l); } if (r < 0) return r; + json_variant_sensitive(w); + r = json_variant_set_field(&h->json, "secret", w); if (r < 0) return r; - strv_free_and_replace(h->pkcs11_pin, e); + strv_free_and_replace(h->token_pin, e); SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w)); return 0; @@ -925,6 +964,37 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h if (r < 0) return r; + if (json_variant_is_blank_object(w)) + r = json_variant_filter(&h->json, STRV_MAKE("secret")); + else { + json_variant_sensitive(w); + + r = json_variant_set_field(&h->json, "secret", w); + } + if (r < 0) + return r; + + h->pkcs11_protected_authentication_path_permitted = b; + + SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w)); + return 0; +} + +int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) { + _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + int r; + + assert(h); + + w = json_variant_ref(json_variant_by_key(h->json, "secret")); + + if (b < 0) + r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted")); + else + r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b); + if (r < 0) + return r; + if (json_variant_is_blank_object(w)) r = json_variant_filter(&h->json, STRV_MAKE("secret")); else @@ -932,7 +1002,7 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h if (r < 0) return r; - h->pkcs11_protected_authentication_path_permitted = b; + h->fido2_user_presence_permitted = b; SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w)); return 0; @@ -1020,12 +1090,22 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) { if (r < 0) return r; - r = user_record_set_pkcs11_pin(h, secret->pkcs11_pin, true); + r = user_record_set_token_pin(h, secret->token_pin, true); if (r < 0) return r; if (secret->pkcs11_protected_authentication_path_permitted >= 0) { - r = user_record_set_pkcs11_protected_authentication_path_permitted(h, secret->pkcs11_protected_authentication_path_permitted); + r = user_record_set_pkcs11_protected_authentication_path_permitted( + h, + secret->pkcs11_protected_authentication_path_permitted); + if (r < 0) + return r; + } + + if (secret->fido2_user_presence_permitted >= 0) { + r = user_record_set_fido2_user_presence_permitted( + h, + secret->fido2_user_presence_permitted); if (r < 0) return r; } diff --git a/src/home/user-record-util.h b/src/home/user-record-util.h index 6afc8df19..245829882 100644 --- a/src/home/user-record-util.h +++ b/src/home/user-record-util.h @@ -47,8 +47,9 @@ int user_record_set_disk_size(UserRecord *h, uint64_t disk_size); int user_record_set_password(UserRecord *h, char **password, bool prepend); int user_record_make_hashed_password(UserRecord *h, char **password, bool extend); int user_record_set_hashed_password(UserRecord *h, char **hashed_password); -int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend); +int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend); int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b); +int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b); int user_record_set_password_change_now(UserRecord *h, int b); int user_record_merge_secret(UserRecord *h, UserRecord *secret); int user_record_good_authentication(UserRecord *h); diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 5596846ed..2d6b2da12 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -12,7 +12,7 @@ #include "alloc-util.h" #include "architecture.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-map-properties.h" #include "hostname-util.h" #include "main-func.h" #include "pretty-print.h" @@ -436,9 +436,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) @@ -446,7 +444,7 @@ static int run(int argc, char *argv[]) { r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); return hostnamectl_main(bus, argc, argv); } diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 21f647149..7f6607a52 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -8,6 +8,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "def.h" #include "env-file-label.h" @@ -25,7 +26,9 @@ #include "parse-util.h" #include "path-util.h" #include "selinux-util.h" +#include "service-util.h" #include "signal-util.h" +#include "stat-util.h" #include "strv.h" #include "user-util.h" #include "util.h" @@ -34,68 +37,89 @@ #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:") enum { - PROP_HOSTNAME, + /* Read from /etc/hostname */ PROP_STATIC_HOSTNAME, + + /* Read from /etc/machine-info */ PROP_PRETTY_HOSTNAME, PROP_ICON_NAME, PROP_CHASSIS, PROP_DEPLOYMENT, PROP_LOCATION, - PROP_KERNEL_NAME, - PROP_KERNEL_RELEASE, - PROP_KERNEL_VERSION, + + /* Read from /etc/os-release (or /usr/lib/os-release) */ PROP_OS_PRETTY_NAME, PROP_OS_CPE_NAME, - PROP_HOME_URL, - _PROP_MAX + PROP_OS_HOME_URL, + _PROP_MAX, + _PROP_INVALID = -1, }; typedef struct Context { char *data[_PROP_MAX]; + + struct stat etc_hostname_stat; + struct stat etc_os_release_stat; + struct stat etc_machine_info_stat; + Hashmap *polkit_registry; - sd_id128_t uuid; - bool has_uuid; } Context; -static void context_reset(Context *c) { +static void context_reset(Context *c, uint64_t mask) { int p; assert(c); - for (p = 0; p < _PROP_MAX; p++) + for (p = 0; p < _PROP_MAX; p++) { + if (!FLAGS_SET(mask, UINT64_C(1) << p)) + continue; + c->data[p] = mfree(c->data[p]); + } } -static void context_clear(Context *c) { +static void context_destroy(Context *c) { assert(c); - context_reset(c); + context_reset(c, UINT64_MAX); bus_verify_polkit_async_registry_free(c->polkit_registry); } -static int context_read_data(Context *c) { +static void context_read_etc_hostname(Context *c) { + struct stat current_stat = {}; int r; - struct utsname u; assert(c); - context_reset(c); + if (stat("/etc/hostname", ¤t_stat) >= 0 && + stat_inode_unmodified(&c->etc_hostname_stat, ¤t_stat)) + return; - assert_se(uname(&u) >= 0); - c->data[PROP_KERNEL_NAME] = strdup(u.sysname); - c->data[PROP_KERNEL_RELEASE] = strdup(u.release); - c->data[PROP_KERNEL_VERSION] = strdup(u.version); - if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] || - !c->data[PROP_KERNEL_VERSION]) - return -ENOMEM; - - c->data[PROP_HOSTNAME] = gethostname_malloc(); - if (!c->data[PROP_HOSTNAME]) - return -ENOMEM; + context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME); r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]); if (r < 0 && r != -ENOENT) - return r; + log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m"); + + c->etc_hostname_stat = current_stat; +} + +static void context_read_machine_info(Context *c) { + struct stat current_stat = {}; + int r; + + assert(c); + + if (stat("/etc/machine-info", ¤t_stat) >= 0 && + stat_inode_unmodified(&c->etc_machine_info_stat, ¤t_stat)) + return; + + context_reset(c, + (UINT64_C(1) << PROP_PRETTY_HOSTNAME) | + (UINT64_C(1) << PROP_ICON_NAME) | + (UINT64_C(1) << PROP_CHASSIS) | + (UINT64_C(1) << PROP_DEPLOYMENT) | + (UINT64_C(1) << PROP_LOCATION)); r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], @@ -104,28 +128,36 @@ static int context_read_data(Context *c) { "DEPLOYMENT", &c->data[PROP_DEPLOYMENT], "LOCATION", &c->data[PROP_LOCATION]); if (r < 0 && r != -ENOENT) - return r; + log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m"); + + c->etc_machine_info_stat = current_stat; +} + +static void context_read_os_release(Context *c) { + struct stat current_stat = {}; + int r; + + assert(c); + + if ((stat("/etc/os-release", ¤t_stat) >= 0 || + stat("/usr/lib/os-release", ¤t_stat) >= 0) && + stat_inode_unmodified(&c->etc_os_release_stat, ¤t_stat)) + return; + + context_reset(c, + (UINT64_C(1) << PROP_OS_PRETTY_NAME) | + (UINT64_C(1) << PROP_OS_CPE_NAME) | + (UINT64_C(1) << PROP_OS_HOME_URL)); r = parse_os_release(NULL, "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], "CPE_NAME", &c->data[PROP_OS_CPE_NAME], - "HOME_URL", &c->data[PROP_HOME_URL], + "HOME_URL", &c->data[PROP_OS_HOME_URL], NULL); if (r < 0 && r != -ENOENT) - return r; + log_warning_errno(r, "Failed to read os-release file, ignoring: %m"); - r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid); - if (r == -ENOENT) - r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &c->uuid); - if (r < 0) - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "Failed to read product UUID, ignoring: %m"); - else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid)) - log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid)); - else - c->has_uuid = true; - - return 0; + c->etc_os_release_stat = current_stat; } static bool valid_chassis(const char *chassis) { @@ -267,12 +299,22 @@ static bool hostname_is_useful(const char *hn) { return !isempty(hn) && !is_localhost(hn); } -static int context_update_kernel_hostname(Context *c) { - const char *static_hn; - const char *hn; +static int context_update_kernel_hostname( + Context *c, + const char *transient_hn) { + + const char *static_hn, *hn; + struct utsname u; assert(c); + if (!transient_hn) { + /* If no transient hostname is passed in, then let's check what is currently set. */ + assert_se(uname(&u) >= 0); + transient_hn = + isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename; + } + static_hn = c->data[PROP_STATIC_HOSTNAME]; /* /etc/hostname with something other than "localhost" @@ -280,9 +322,9 @@ static int context_update_kernel_hostname(Context *c) { if (hostname_is_useful(static_hn)) hn = static_hn; - /* ... the transient host name, (ie: DHCP) comes next ... */ - else if (!isempty(c->data[PROP_HOSTNAME])) - hn = c->data[PROP_HOSTNAME]; + /* ... the transient hostname, (ie: DHCP) comes next ... */ + else if (!isempty(transient_hn)) + hn = transient_hn; /* ... fallback to static "localhost.*" ignored above ... */ else if (!isempty(static_hn)) @@ -301,7 +343,6 @@ static int context_update_kernel_hostname(Context *c) { } static int context_write_data_static_hostname(Context *c) { - assert(c); if (isempty(c->data[PROP_STATIC_HOSTNAME])) { @@ -365,6 +406,94 @@ static int context_write_data_machine_info(Context *c) { return write_env_file_label("/etc/machine-info", l); } +static int property_get_hostname( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *current = NULL; + int r; + + r = gethostname_strict(¤t); + if (r == -ENXIO) + return sd_bus_message_append(reply, "s", FALLBACK_HOSTNAME); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "s", current); +} + +static int property_get_static_hostname( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Context *c = userdata; + assert(c); + + context_read_etc_hostname(c); + + return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]); +} + +static int property_get_machine_info_field( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + sd_bus_slot *slot; + Context *c; + + /* Acquire the context object without this property's userdata offset added. Explanation: we want + * access to two pointers here: a) the main context object we cache all properties in, and b) the + * pointer to the property field inside the context object that we are supposed to update and + * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us + * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we + * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from + * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot + * object is (which encapsulates the vtable registration), and then query the 'userdata' field + * directly off it. */ + assert_se(slot = sd_bus_get_current_slot(bus)); + assert_se(c = sd_bus_slot_get_userdata(slot)); + + context_read_machine_info(c); + + return sd_bus_message_append(reply, "s", *(char**) userdata); +} + +static int property_get_os_release_field( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + sd_bus_slot *slot; + Context *c; + + /* As above, acquire the current context without this property's userdata offset added. */ + assert_se(slot = sd_bus_get_current_slot(bus)); + assert_se(c = sd_bus_slot_get_userdata(slot)); + + context_read_os_release(c); + + return sd_bus_message_append(reply, "s", *(char**) userdata); +} + static int property_get_icon_name( sd_bus *bus, const char *path, @@ -378,6 +507,8 @@ static int property_get_icon_name( Context *c = userdata; const char *name; + context_read_machine_info(c); + if (isempty(c->data[PROP_ICON_NAME])) name = n = context_fallback_icon_name(c); else @@ -401,6 +532,8 @@ static int property_get_chassis( Context *c = userdata; const char *name; + context_read_machine_info(c); + if (isempty(c->data[PROP_CHASSIS])) name = fallback_chassis(); else @@ -409,11 +542,27 @@ static int property_get_chassis( return sd_bus_message_append(reply, "s", name); } +static int property_get_uname_field( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + struct utsname u; + + assert_se(uname(&u) >= 0); + + return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata)); +} + static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; const char *name; - int interactive; - int r; + int interactive, r; + struct utsname u; assert(m); assert(c); @@ -422,6 +571,8 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * if (r < 0) return r; + context_read_etc_hostname(c); + if (isempty(name)) name = c->data[PROP_STATIC_HOSTNAME]; @@ -431,7 +582,8 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * if (!hostname_is_valid(name, false)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); - if (streq_ptr(name, c->data[PROP_HOSTNAME])) + assert_se(uname(&u) >= 0); + if (streq_ptr(name, u.nodename)) return sd_bus_reply_method_return(m, NULL); r = bus_verify_polkit_async( @@ -448,17 +600,13 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = free_and_strdup(&c->data[PROP_HOSTNAME], name); - if (r < 0) - return r; - - r = context_update_kernel_hostname(c); + r = context_update_kernel_hostname(c, name); if (r < 0) { - log_error_errno(r, "Failed to set host name: %m"); + log_error_errno(r, "Failed to set hostname: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); } - log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME])); + log_info("Changed hostname to '%s'", name); (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL); @@ -480,6 +628,8 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ name = empty_to_null(name); + context_read_etc_hostname(c); + if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) return sd_bus_reply_method_return(m, NULL); @@ -504,19 +654,19 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ if (r < 0) return r; - r = context_update_kernel_hostname(c); + r = context_update_kernel_hostname(c, NULL); if (r < 0) { - log_error_errno(r, "Failed to set host name: %m"); + log_error_errno(r, "Failed to set hostname: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m"); } r = context_write_data_static_hostname(c); if (r < 0) { - log_error_errno(r, "Failed to write static host name: %m"); + log_error_errno(r, "Failed to write static hostname: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m"); } - log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); + log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL); @@ -537,6 +687,8 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess name = empty_to_null(name); + context_read_machine_info(c); + if (streq_ptr(name, c->data[prop])) return sd_bus_reply_method_return(m, NULL); @@ -547,7 +699,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess if (prop == PROP_ICON_NAME && !filename_is_valid(name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name); if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name); if (prop == PROP_CHASSIS && !valid_chassis(name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name); if (prop == PROP_DEPLOYMENT && !valid_deployment(name)) @@ -585,7 +737,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess } log_info("Changed %s to '%s'", - prop == PROP_PRETTY_HOSTNAME ? "pretty host name" : + prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" : prop == PROP_DEPLOYMENT ? "deployment" : prop == PROP_LOCATION ? "location" : prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop])); @@ -625,13 +777,27 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error * static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Context *c = userdata; + bool has_uuid = false; int interactive, r; + sd_id128_t uuid; assert(m); assert(c); - if (!c->has_uuid) - return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid"); + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + if (r == -ENOENT) + r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read product UUID, ignoring: %m"); + else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) + log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid)); + else + has_uuid = true; + + if (!has_uuid) + return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, + "Failed to read product UUID from firmware."); r = sd_bus_message_read(m, "b", &interactive); if (r < 0) @@ -655,7 +821,7 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err if (r < 0) return r; - r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid)); + r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid)); if (r < 0) return r; @@ -664,30 +830,86 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err static const sd_bus_vtable hostname_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Location", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelVersion", "s", property_get_uname_field, offsetof(struct utsname, version), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD_WITH_NAMES("SetHostname", + "sb", + SD_BUS_PARAM(hostname) + SD_BUS_PARAM(interactive), + NULL,, + method_set_hostname, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetStaticHostname", + "sb", + SD_BUS_PARAM(hostname) + SD_BUS_PARAM(interactive), + NULL,, + method_set_static_hostname, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname", + "sb", + SD_BUS_PARAM(hostname) + SD_BUS_PARAM(interactive), + NULL,, + method_set_pretty_hostname, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetIconName", + "sb", + SD_BUS_PARAM(icon) + SD_BUS_PARAM(interactive), + NULL,, + method_set_icon_name, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetChassis", + "sb", + SD_BUS_PARAM(chassis) + SD_BUS_PARAM(interactive), + NULL,, + method_set_chassis, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetDeployment", + "sb", + SD_BUS_PARAM(deployment) + SD_BUS_PARAM(interactive), + NULL,, + method_set_deployment, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetLocation", + "sb", + SD_BUS_PARAM(location) + SD_BUS_PARAM(interactive), + NULL,, + method_set_location, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetProductUUID", + "b", + SD_BUS_PARAM(interactive), + "ay", + SD_BUS_PARAM(uuid), + method_get_product_uuid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, }; +static const BusObjectImplementation manager_object = { + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + .vtables = BUS_VTABLES(hostname_vtable), +}; + static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -700,9 +922,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to get system bus connection: %m"); - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c); + r = bus_add_implementation(bus, &manager_object, c); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); + return r; + + r = bus_log_control_api_register(bus); + if (r < 0) + return r; r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL); if (r < 0) @@ -718,20 +944,26 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { } static int run(int argc, char *argv[]) { - _cleanup_(context_clear) Context context = {}; + _cleanup_(context_destroy) Context context = {}; _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; log_setup_service(); - umask(0022); - mac_selinux_init(); + r = service_parse_argv("systemd-hostnamed.service", + "Manage the system hostname and related metadata.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) { - log_error("This program takes no arguments."); - return -EINVAL; - } + umask(0022); + + r = mac_selinux_init(); + if (r < 0) + return r; assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); @@ -753,10 +985,6 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - r = context_read_data(&context); - if (r < 0) - return log_error_errno(r, "Failed to read hostname and machine information: %m"); - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); diff --git a/src/hostname/org.freedesktop.hostname1.policy b/src/hostname/org.freedesktop.hostname1.policy index 5bedc0b69..5969a82b2 100644 --- a/src/hostname/org.freedesktop.hostname1.policy +++ b/src/hostname/org.freedesktop.hostname1.policy @@ -17,8 +17,8 @@ http://www.freedesktop.org/wiki/Software/systemd - Set host name - Authentication is required to set the local host name. + Set hostname + Authentication is required to set the local hostname. auth_admin_keep auth_admin_keep @@ -27,8 +27,8 @@ - Set static host name - Authentication is required to set the statically configured local host name, as well as the pretty host name. + Set static hostname + Authentication is required to set the statically configured local hostname, as well as the pretty hostname. auth_admin_keep auth_admin_keep diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index 651647b3f..eb038a8b5 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -125,7 +125,9 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; - mac_selinux_init(); + r = mac_selinux_init(); + if (r < 0) + return r; return hwdb_main(argc, argv); } diff --git a/src/id128/id128.c b/src/id128/id128.c index 19435f80f..236043bf8 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -139,7 +139,7 @@ static int verb_show(int argc, char **argv, void *userdata) { if (table) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); } return 0; @@ -249,9 +249,7 @@ static int id128_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 5f21033db..261fbece7 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -166,9 +166,8 @@ CurlGlue *curl_glue_unref(CurlGlue *g) { if (g->curl) curl_multi_cleanup(g->curl); - while ((io = hashmap_steal_first(g->ios))) { + while ((io = hashmap_steal_first(g->ios))) sd_event_source_unref(io); - } hashmap_free(g->ios); diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 1e50d31bc..5d2bf22fb 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -220,13 +220,10 @@ static int tar_import_fork_tar(TarImport *i) { (void) mkdir_parents_label(i->temp_path, 0700); - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path); - else + r = btrfs_subvol_make_fallback(i->temp_path, 0755); + if (r < 0) + return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path); + if (r > 0) /* actually btrfs subvol */ (void) import_assign_pool_quota_and_warn(i->temp_path); i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); diff --git a/src/import/importd.c b/src/import/importd.c index 6e868ee50..038dd3a6c 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -7,6 +7,8 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "def.h" #include "fd-util.h" @@ -20,6 +22,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "service-util.h" #include "signal-util.h" #include "socket-util.h" #include "stat-util.h" @@ -487,11 +490,13 @@ static int transfer_start(Transfer *t) { t->stdin_fd = safe_close(t->stdin_fd); - r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t); + r = sd_event_add_child(t->manager->event, &t->pid_event_source, + t->pid, WEXITED, transfer_on_pid, t); if (r < 0) return r; - r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t); + r = sd_event_add_io(t->manager->event, &t->log_event_source, + t->log_fd, EPOLLIN, transfer_on_log, t); if (r < 0) return r; @@ -545,20 +550,16 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void .iov_base = buf, .iov_len = sizeof(buf)-1, }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = &control, .msg_controllen = sizeof(control), }; - struct ucred *ucred = NULL; + struct ucred *ucred; Manager *m = userdata; - struct cmsghdr *cmsg; char *p, *e; Transfer *t; Iterator i; @@ -573,17 +574,12 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void cmsg_close_all(&msghdr); - CMSG_FOREACH(cmsg, &msghdr) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) - ucred = (struct ucred*) CMSG_DATA(cmsg); - if (msghdr.msg_flags & MSG_TRUNC) { log_warning("Got overly long notification datagram, ignoring."); return 0; } + ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); if (!ucred || ucred->pid <= 0) { log_warning("Got notification datagram lacking credential information, ignoring."); return 0; @@ -662,7 +658,8 @@ static int manager_new(Manager **ret) { if (r < 0) return r; - r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m); + r = sd_event_add_io(m->event, &m->notify_event_source, + m->notify_fd, EPOLLIN, manager_on_notify, m); if (r < 0) return r; @@ -723,13 +720,15 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ return -EINVAL; if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Local name %s is invalid", local); r = setup_machine_directory(error); if (r < 0) return r; - type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW; + type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? + TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW; r = transfer_new(m, &t); if (r < 0) @@ -791,7 +790,8 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e return r; if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Local name %s is invalid", local); r = setup_machine_directory(error); if (r < 0) @@ -855,7 +855,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ return r; if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Local name %s is invalid", local); if (fstat(fd, &st) < 0) return -errno; @@ -863,7 +864,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode)) return -EINVAL; - type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; + type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? + TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; r = transfer_new(m, &t); if (r < 0) @@ -927,28 +929,33 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er return r; if (!http_url_is_valid(remote)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "URL %s is invalid", remote); if (isempty(local)) local = NULL; else if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Local name %s is invalid", local); if (isempty(verify)) v = IMPORT_VERIFY_SIGNATURE; else v = import_verify_from_string(verify); if (v < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Unknown verification mode %s", verify); r = setup_machine_directory(error); if (r < 0) return r; - type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW; + type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? + TRANSFER_PULL_TAR : TRANSFER_PULL_RAW; if (manager_find(m, type, remote)) - return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote); + return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, + "Transfer for %s already in progress.", remote); r = transfer_new(m, &t); if (r < 0) @@ -1108,36 +1115,14 @@ static int property_get_progress( static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify); -static const sd_bus_vtable transfer_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), - SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("LogMessage", "us", 0), - SD_BUS_VTABLE_END, -}; +static int transfer_object_find( + sd_bus *bus, + const char *path, + const char *interface, + void *userdata, + void **found, + sd_bus_error *error) { -static const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ImportFileSystem", "hsbb", "uo", method_import_fs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("TransferNew", "uo", 0), - SD_BUS_SIGNAL("TransferRemoved", "uos", 0), - SD_BUS_VTABLE_END, -}; - -static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { Manager *m = userdata; Transfer *t; const char *p; @@ -1166,7 +1151,13 @@ static int transfer_object_find(sd_bus *bus, const char *path, const char *inter return 1; } -static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int transfer_node_enumerator( + sd_bus *bus, + const char *path, + void *userdata, + char ***nodes, + sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; Transfer *t; @@ -1191,22 +1182,159 @@ static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdat return 1; } +static const sd_bus_vtable transfer_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), + + SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL_WITH_NAMES("LogMessage", + "us", + SD_BUS_PARAM(priority) + SD_BUS_PARAM(line), + 0), + + SD_BUS_VTABLE_END, +}; + +static const BusObjectImplementation transfer_object = { + "/org/freedesktop/import1/transfer", + "org.freedesktop.import1.Transfer", + .fallback_vtables = BUS_FALLBACK_VTABLES({transfer_vtable, transfer_object_find}), + .node_enumerator = transfer_node_enumerator, +}; + +static const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_METHOD_WITH_NAMES("ImportTar", + "hsbb", + SD_BUS_PARAM(fd) + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(force) + SD_BUS_PARAM(read_only), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_import_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ImportRaw", + "hsbb", + SD_BUS_PARAM(fd) + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(force) + SD_BUS_PARAM(read_only), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_import_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ImportFileSystem", + "hsbb", + SD_BUS_PARAM(fd) + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(force) + SD_BUS_PARAM(read_only), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_import_fs, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ExportTar", + "shs", + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(fd) + SD_BUS_PARAM(format), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_export_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ExportRaw", + "shs", + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(fd) + SD_BUS_PARAM(format), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_export_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PullTar", + "sssb", + SD_BUS_PARAM(url) + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(verify_mode) + SD_BUS_PARAM(force), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_pull_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PullRaw", + "sssb", + SD_BUS_PARAM(url) + SD_BUS_PARAM(local_name) + SD_BUS_PARAM(verify_mode) + SD_BUS_PARAM(force), + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + method_pull_tar_or_raw, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListTransfers", + NULL,, + "a(usssdo)", + SD_BUS_PARAM(transfers), + method_list_transfers, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CancelTransfer", + "u", + SD_BUS_PARAM(transfer_id), + NULL,, + method_cancel_transfer, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL_WITH_NAMES("TransferNew", + "uo", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("TransferRemoved", + "uos", + SD_BUS_PARAM(transfer_id) + SD_BUS_PARAM(transfer_path) + SD_BUS_PARAM(result), + 0), + + SD_BUS_VTABLE_END, +}; + +static const BusObjectImplementation manager_object = { + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + .vtables = BUS_VTABLES(manager_vtable), + .children = BUS_IMPLEMENTATIONS(&transfer_object), +}; + static int manager_add_bus_objects(Manager *m) { int r; assert(m); - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m); + r = bus_add_implementation(m->bus, &manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); + return r; - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m); + r = bus_log_control_api_register(m->bus); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add transfer enumerator: %m"); + return r; r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL); if (r < 0) @@ -1243,12 +1371,15 @@ static int run(int argc, char *argv[]) { log_setup_service(); - umask(0022); + r = service_parse_argv("systemd-importd.service", + "VM and container image import and export service.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) { - log_error("This program takes no arguments."); - return -EINVAL; - } + umask(0022); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 3930578a8..ede28bee1 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -415,13 +415,10 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) { mkdir_parents_label(i->temp_path, 0700); - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path); - else + r = btrfs_subvol_make_fallback(i->temp_path, 0755); + if (r < 0) + return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path); + if (r > 0) /* actually btrfs subvol */ (void) import_assign_pool_quota_and_warn(i->temp_path); j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index 150d0fb19..7eee19766 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -14,12 +14,14 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" +#include "daemon-util.h" #include "def.h" #include "fd-util.h" #include "format-util.h" #include "initreq.h" #include "list.h" #include "log.h" +#include "main-func.h" #include "memory-util.h" #include "process-util.h" #include "special.h" @@ -68,11 +70,9 @@ static const char *translate_runlevel(int runlevel, bool *isolate) { { '6', SPECIAL_REBOOT_TARGET, false }, }; - unsigned i; - assert(isolate); - for (i = 0; i < ELEMENTSOF(table); i++) + for (size_t i = 0; i < ELEMENTSOF(table); i++) if (table[i].runlevel == runlevel) { *isolate = table[i].isolate; if (runlevel == '6' && kexec_loaded()) @@ -228,6 +228,7 @@ static void fifo_free(Fifo *f) { free(f); } +DEFINE_TRIVIAL_CLEANUP_FUNC(Fifo*, fifo_free); static void server_done(Server *s) { assert(s); @@ -241,77 +242,49 @@ static void server_done(Server *s) { static int server_init(Server *s, unsigned n_sockets) { int r; - unsigned i; + + /* This function will leave s partially initialized on failure. Caller needs to clean up. */ assert(s); assert(n_sockets > 0); - zero(*s); - s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (s->epoll_fd < 0) { - r = log_error_errno(errno, - "Failed to create epoll object: %m"); - goto fail; - } + if (s->epoll_fd < 0) + return log_error_errno(errno, "Failed to create epoll object: %m"); - for (i = 0; i < n_sockets; i++) { - struct epoll_event ev; - Fifo *f; - int fd; - - fd = SD_LISTEN_FDS_START+i; + for (unsigned i = 0; i < n_sockets; i++) { + _cleanup_(fifo_freep) Fifo *f = NULL; + int fd = SD_LISTEN_FDS_START + i; r = sd_is_fifo(fd, NULL); - if (r < 0) { - log_error_errno(r, "Failed to determine file descriptor type: %m"); - goto fail; - } - - if (!r) { - log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to determine file descriptor type: %m"); + if (!r) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong file descriptor type."); f = new0(Fifo, 1); - if (!f) { - r = -ENOMEM; - log_error_errno(errno, "Failed to create fifo object: %m"); - goto fail; - } + if (!f) + return log_oom(); - f->fd = -1; + struct epoll_event ev = { + .events = EPOLLIN, + .data.ptr = f, + }; - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = f; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - fifo_free(f); - log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); - goto fail; - } + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) + return log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); f->fd = fd; - LIST_PREPEND(fifo, s->fifos, f); f->server = s; + LIST_PREPEND(fifo, s->fifos, TAKE_PTR(f)); s->n_fifos++; } r = bus_connect_system_systemd(&s->bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - r = -EIO; - goto fail; - } + if (r < 0) + return log_error_errno(r, "Failed to get D-Bus connection: %m"); return 0; - -fail: - server_done(s); - - return r; } static int process_event(Server *s, struct epoll_event *ev) { @@ -335,43 +308,33 @@ static int process_event(Server *s, struct epoll_event *ev) { return 0; } -int main(int argc, char *argv[]) { - Server server; - int r = EXIT_FAILURE, n; +static int run(int argc, char *argv[]) { + _cleanup_(server_done) Server server = { .epoll_fd = -1 }; + _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; + int r, n; - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } + if (argc > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program does not take arguments."); log_setup_service(); umask(0022); n = sd_listen_fds(true); - if (n < 0) { - log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); - return EXIT_FAILURE; - } + if (n < 0) + return log_error_errno(errno, + "Failed to read listening file descriptors from environment: %m"); - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } + if (n <= 0 || n > SERVER_FD_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No or too many file descriptors passed."); - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; + r = server_init(&server, (unsigned) n); + if (r < 0) + return r; - log_debug("systemd-initctl running as pid "PID_FMT, getpid_cached()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); + notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING); while (!server.quit) { struct epoll_event event; @@ -381,27 +344,17 @@ int main(int argc, char *argv[]) { if (k < 0) { if (errno == EINTR) continue; - log_error_errno(errno, "epoll_wait() failed: %m"); - goto fail; + return log_error_errno(errno, "epoll_wait() failed: %m"); } - - if (k <= 0) + if (k == 0) break; - if (process_event(&server, &event) < 0) - goto fail; + r = process_event(&server, &event); + if (r < 0) + return r; } - r = EXIT_SUCCESS; - - log_debug("systemd-initctl stopped as pid "PID_FMT, getpid_cached()); - -fail: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - server_done(&server); - - return r; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 825f43ce6..48106d1bd 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -123,17 +123,15 @@ static int request_meta_ensure_tmp(RequestMeta *m) { if (m->tmp) rewind(m->tmp); else { - int fd; + _cleanup_close_ int fd = -1; fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC); if (fd < 0) return fd; - m->tmp = fdopen(fd, "w+"); - if (!m->tmp) { - safe_close(fd); + m->tmp = take_fdopen(&fd, "w+"); + if (!m->tmp) return -errno; - } } return 0; @@ -908,7 +906,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_key_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file specified twice"); - r = read_full_file(optarg, &arg_key_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_key_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read key file: %m"); assert(arg_key_pem); @@ -918,7 +916,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_cert_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Certificate file specified twice"); - r = read_full_file(optarg, &arg_cert_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_cert_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate file: %m"); assert(arg_cert_pem); @@ -929,7 +927,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_trust_pem) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "CA certificate file specified twice"); - r = read_full_file(optarg, &arg_trust_pem, NULL); + r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_trust_pem, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file: %m"); assert(arg_trust_pem); diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 1c8e7ab63..77dfdefd6 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -762,10 +762,14 @@ static int parse_config(void) { {} }; - return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf", - CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), - "Remote\0", config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/journal-remote.conf", + CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), + "Remote\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); } static int help(void) { @@ -1073,12 +1077,12 @@ static int parse_argv(int argc, char *argv[]) { static int load_certificates(char **key, char **cert, char **trust) { int r; - r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL); + r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, key, NULL); if (r < 0) return log_error_errno(r, "Failed to read key from file '%s': %m", arg_key ?: PRIV_KEY_FILE); - r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL); + r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, cert, NULL); if (r < 0) return log_error_errno(r, "Failed to read certificate from file '%s': %m", arg_cert ?: CERT_FILE); @@ -1086,7 +1090,7 @@ static int load_certificates(char **key, char **cert, char **trust) { if (arg_trust_all) log_info("Certificate checking disabled."); else { - r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL); + r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, trust, NULL); if (r < 0) return log_error_errno(r, "Failed to read CA certificate file '%s': %m", arg_trust ?: TRUST_FILE); @@ -1100,13 +1104,13 @@ static int load_certificates(char **key, char **cert, char **trust) { } static int run(int argc, char **argv) { - _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(journal_remote_server_destroy) RemoteServer s = {}; + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; int r; log_show_color(true); - log_parse_environment(); + log_parse_environment_cli(); /* The journal merging logic potentially needs a lot of fds. */ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 221421af4..13ca90f95 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -23,6 +23,7 @@ #include "main-func.h" #include "mkdir.h" #include "parse-util.h" +#include "path-util.h" #include "pretty-print.h" #include "process-util.h" #include "rlimit-util.h" @@ -240,14 +241,14 @@ int start_upload(Uploader *u, "systemd-journal-upload " GIT_VERSION, LOG_WARNING, ); - if (arg_key || startswith(u->url, "https://")) { + if (!streq_ptr(arg_key, "-") && (arg_key || startswith(u->url, "https://"))) { easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE, LOG_ERR, return -EXFULL); easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE, LOG_ERR, return -EXFULL); } - if (streq_ptr(arg_trust, "all")) + if (STRPTR_IN_SET(arg_trust, "-", "all")) easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0, LOG_ERR, return -EUCLEAN); else if (arg_trust || startswith(u->url, "https://")) @@ -515,18 +516,63 @@ static int perform_upload(Uploader *u) { return update_cursor_state(u); } +static int config_parse_path_or_ignore( + 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 *n = NULL; + bool fatal = ltype; + char **s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) + goto finalize; + + n = strdup(rvalue); + if (!n) + return log_oom(); + + if (streq(n, "-")) + goto finalize; + + r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue); + if (r < 0) + return fatal ? -ENOEXEC : 0; + +finalize: + return free_and_replace(*s, n); +} + static int parse_config(void) { const ConfigTableItem items[] = { - { "Upload", "URL", config_parse_string, 0, &arg_url }, - { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key }, - { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert }, - { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, - {}}; + { "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 }, + {} + }; - return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf", - CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), - "Upload\0", config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/journal-upload.conf", + CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), + "Upload\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); } static int help(void) { @@ -769,13 +815,13 @@ static int open_journal(sd_journal **j) { } static int run(int argc, char **argv) { - _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(destroy_uploader) Uploader u = {}; + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; bool use_journal; int r; log_show_color(true); - log_parse_environment(); + log_parse_environment_cli(); /* The journal merging logic potentially needs a lot of fds. */ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index 939af1257..027f2c8ff 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -78,10 +78,9 @@ int mhd_respondf(struct MHD_Connection *connection, errno = -error; fmt = strjoina(format, "\n"); va_start(ap, format); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" + DISABLE_WARNING_FORMAT_NONLITERAL; r = vasprintf(&m, fmt, ap); -#pragma GCC diagnostic pop + REENABLE_WARNING; va_end(ap); if (r < 0) diff --git a/src/journal/cat.c b/src/journal/cat.c index 500b67450..2faaa2e28 100644 --- a/src/journal/cat.c +++ b/src/journal/cat.c @@ -129,9 +129,7 @@ static int run(int argc, char *argv[]) { _cleanup_close_ int outfd = -1, errfd = -1, saved_stderr = -1; int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 70b2c8b46..b2589271c 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -55,7 +55,7 @@ typedef struct CatalogItem { static void catalog_hash_func(const CatalogItem *i, struct siphash *state) { siphash24_compress(&i->id, sizeof(i->id), state); - siphash24_compress(i->language, strlen(i->language), state); + siphash24_compress_string(i->language, state); } static int catalog_compare_func(const CatalogItem *a, const CatalogItem *b) { diff --git a/src/journal/compress.c b/src/journal/compress.c index 4e00e4fc5..a59c2b7a8 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -16,6 +16,11 @@ #include #endif +#if HAVE_ZSTD +#include +#include +#endif + #include "alloc-util.h" #include "compress.h" #include "fd-util.h" @@ -33,11 +38,30 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionConte DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext); #endif +#if HAVE_ZSTD +DEFINE_TRIVIAL_CLEANUP_FUNC(ZSTD_CCtx *, ZSTD_freeCCtx); +DEFINE_TRIVIAL_CLEANUP_FUNC(ZSTD_DCtx *, ZSTD_freeDCtx); + +static int zstd_ret_to_errno(size_t ret) { + switch (ZSTD_getErrorCode(ret)) { + case ZSTD_error_dstSize_tooSmall: + return -ENOBUFS; + case ZSTD_error_memory_allocation: + return -ENOMEM; + default: + return -EBADMSG; + } +} +#endif + #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t)) static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = { - [OBJECT_COMPRESSED_XZ] = "XZ", - [OBJECT_COMPRESSED_LZ4] = "LZ4", + [OBJECT_COMPRESSED_XZ] = "XZ", + [OBJECT_COMPRESSED_LZ4] = "LZ4", + [OBJECT_COMPRESSED_ZSTD] = "ZSTD", + /* If we add too many more entries here, it's going to grow quite large (and be mostly sparse), since + * the array key is actually a bitmask, not a plain enum */ }; DEFINE_STRING_TABLE_LOOKUP(object_compressed, int); @@ -110,6 +134,29 @@ int compress_blob_lz4(const void *src, uint64_t src_size, #endif } +int compress_blob_zstd( + const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { +#if HAVE_ZSTD + size_t k; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size > 0); + assert(dst_size); + + k = ZSTD_compress(dst, dst_alloc_size, src, src_size, 0); + if (ZSTD_isError(k)) + return zstd_ret_to_errno(k); + + *dst_size = k; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + int decompress_blob_xz(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { @@ -210,17 +257,78 @@ int decompress_blob_lz4(const void *src, uint64_t src_size, #endif } -int decompress_blob(int compression, - const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { - if (compression == OBJECT_COMPRESSED_XZ) - return decompress_blob_xz(src, src_size, - dst, dst_alloc_size, dst_size, dst_max); - else if (compression == OBJECT_COMPRESSED_LZ4) - return decompress_blob_lz4(src, src_size, - dst, dst_alloc_size, dst_size, dst_max); - else +int decompress_blob_zstd( + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t *dst_size, size_t dst_max) { + +#if HAVE_ZSTD + uint64_t size; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + size = ZSTD_getFrameContentSize(src, src_size); + if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN)) return -EBADMSG; + + if (dst_max > 0 && size > dst_max) + size = dst_max; + if (size > SIZE_MAX) + return -E2BIG; + + if (!(greedy_realloc(dst, dst_alloc_size, MAX(ZSTD_DStreamOutSize(), size), 1))) + return -ENOMEM; + + _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx(); + if (!dctx) + return -ENOMEM; + + ZSTD_inBuffer input = { + .src = src, + .size = src_size, + }; + ZSTD_outBuffer output = { + .dst = *dst, + .size = *dst_alloc_size, + }; + + size_t k = ZSTD_decompressStream(dctx, &output, &input); + if (ZSTD_isError(k)) { + log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k)); + return zstd_ret_to_errno(k); + } + assert(output.pos >= size); + + *dst_size = size; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_blob( + int compression, + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + + if (compression == OBJECT_COMPRESSED_XZ) + return decompress_blob_xz( + src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else if (compression == OBJECT_COMPRESSED_LZ4) + return decompress_blob_lz4( + src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else if (compression == OBJECT_COMPRESSED_ZSTD) + return decompress_blob_zstd( + src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else + return -EPROTONOSUPPORT; } int decompress_startswith_xz(const void *src, uint64_t src_size, @@ -344,21 +452,83 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, #endif } -int decompress_startswith(int compression, - const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra) { +int decompress_startswith_zstd( + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { +#if HAVE_ZSTD + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + uint64_t size = ZSTD_getFrameContentSize(src, src_size); + if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN)) + return -EBADMSG; + + if (size < prefix_len + 1) + return 0; /* Decompressed text too short to match the prefix and extra */ + + _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx(); + if (!dctx) + return -ENOMEM; + + if (!(greedy_realloc(buffer, buffer_size, MAX(ZSTD_DStreamOutSize(), prefix_len + 1), 1))) + return -ENOMEM; + + ZSTD_inBuffer input = { + .src = src, + .size = src_size, + }; + ZSTD_outBuffer output = { + .dst = *buffer, + .size = *buffer_size, + }; + size_t k; + + k = ZSTD_decompressStream(dctx, &output, &input); + if (ZSTD_isError(k)) { + log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k)); + return zstd_ret_to_errno(k); + } + assert(output.pos >= prefix_len + 1); + + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_startswith( + int compression, + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { + if (compression == OBJECT_COMPRESSED_XZ) - return decompress_startswith_xz(src, src_size, - buffer, buffer_size, - prefix, prefix_len, - extra); + return decompress_startswith_xz( + src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else if (compression == OBJECT_COMPRESSED_LZ4) - return decompress_startswith_lz4(src, src_size, - buffer, buffer_size, - prefix, prefix_len, - extra); + return decompress_startswith_lz4( + src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else if (compression == OBJECT_COMPRESSED_ZSTD) + return decompress_startswith_zstd( + src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); else return -EBADMSG; } @@ -668,12 +838,223 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { #endif } +int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) { +#if HAVE_ZSTD + _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL; + _cleanup_free_ void *in_buff = NULL, *out_buff = NULL; + size_t in_allocsize, out_allocsize; + size_t z; + uint64_t left = max_bytes, in_bytes = 0; + + assert(fdf >= 0); + assert(fdt >= 0); + + /* Create the context and buffers */ + in_allocsize = ZSTD_CStreamInSize(); + out_allocsize = ZSTD_CStreamOutSize(); + in_buff = malloc(in_allocsize); + out_buff = malloc(out_allocsize); + cctx = ZSTD_createCCtx(); + if (!cctx || !out_buff || !in_buff) + return -ENOMEM; + + z = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1); + if (ZSTD_isError(z)) + log_debug("Failed to enable ZSTD checksum, ignoring: %s", ZSTD_getErrorName(z)); + + /* This loop read from the input file, compresses that entire chunk, + * and writes all output produced to the output file. + */ + for (;;) { + bool is_last_chunk; + ZSTD_inBuffer input = { + .src = in_buff, + .size = 0, + .pos = 0 + }; + ssize_t red; + + red = loop_read(fdf, in_buff, in_allocsize, true); + if (red < 0) + return red; + is_last_chunk = red == 0; + + in_bytes += (size_t) red; + input.size = (size_t) red; + + for (bool finished = false; !finished;) { + ZSTD_outBuffer output = { + .dst = out_buff, + .size = out_allocsize, + .pos = 0 + }; + size_t remaining; + ssize_t wrote; + + /* Compress into the output buffer and write all of the + * output to the file so we can reuse the buffer next + * iteration. + */ + remaining = ZSTD_compressStream2( + cctx, &output, &input, + is_last_chunk ? ZSTD_e_end : ZSTD_e_continue); + + if (ZSTD_isError(remaining)) { + log_debug("ZSTD encoder failed: %s", ZSTD_getErrorName(remaining)); + return zstd_ret_to_errno(remaining); + } + + if (left < output.pos) + return -EFBIG; + + wrote = loop_write(fdt, output.dst, output.pos, 1); + if (wrote < 0) + return wrote; + + left -= output.pos; + + /* If we're on the last chunk we're finished when zstd + * returns 0, which means its consumed all the input AND + * finished the frame. Otherwise, we're finished when + * we've consumed all the input. + */ + finished = is_last_chunk ? (remaining == 0) : (input.pos == input.size); + } + + /* zstd only returns 0 when the input is completely consumed */ + assert(input.pos == input.size); + if (is_last_chunk) + break; + } + + log_debug( + "ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)", + in_bytes, + max_bytes - left, + (double) (max_bytes - left) / in_bytes * 100); + + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) { +#if HAVE_ZSTD + _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL; + _cleanup_free_ void *in_buff = NULL, *out_buff = NULL; + size_t in_allocsize, out_allocsize; + size_t last_result = 0; + uint64_t left = max_bytes, in_bytes = 0; + + assert(fdf >= 0); + assert(fdt >= 0); + + /* Create the context and buffers */ + in_allocsize = ZSTD_DStreamInSize(); + out_allocsize = ZSTD_DStreamOutSize(); + in_buff = malloc(in_allocsize); + out_buff = malloc(out_allocsize); + dctx = ZSTD_createDCtx(); + if (!dctx || !out_buff || !in_buff) + return -ENOMEM; + + /* This loop assumes that the input file is one or more concatenated + * zstd streams. This example won't work if there is trailing non-zstd + * data at the end, but streaming decompression in general handles this + * case. ZSTD_decompressStream() returns 0 exactly when the frame is + * completed, and doesn't consume input after the frame. + */ + for (;;) { + bool has_error = false; + ZSTD_inBuffer input = { + .src = in_buff, + .size = 0, + .pos = 0 + }; + ssize_t red; + + red = loop_read(fdf, in_buff, in_allocsize, true); + if (red < 0) + return red; + if (red == 0) + break; + + in_bytes += (size_t) red; + input.size = (size_t) red; + input.pos = 0; + + /* Given a valid frame, zstd won't consume the last byte of the + * frame until it has flushed all of the decompressed data of + * the frame. So input.pos < input.size means frame is not done + * or there is still output available. + */ + while (input.pos < input.size) { + ZSTD_outBuffer output = { + .dst = out_buff, + .size = out_allocsize, + .pos = 0 + }; + ssize_t wrote; + /* The return code is zero if the frame is complete, but + * there may be multiple frames concatenated together. + * Zstd will automatically reset the context when a + * frame is complete. Still, calling ZSTD_DCtx_reset() + * can be useful to reset the context to a clean state, + * for instance if the last decompression call returned + * an error. + */ + last_result = ZSTD_decompressStream(dctx, &output, &input); + if (ZSTD_isError(last_result)) { + has_error = true; + break; + } + + if (left < output.pos) + return -EFBIG; + + wrote = loop_write(fdt, output.dst, output.pos, 1); + if (wrote < 0) + return wrote; + + left -= output.pos; + } + if (has_error) + break; + } + + if (in_bytes == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read"); + + if (last_result != 0) { + /* The last return value from ZSTD_decompressStream did not end + * on a frame, but we reached the end of the file! We assume + * this is an error, and the input was truncated. + */ + log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(last_result)); + return zstd_ret_to_errno(last_result); + } + + log_debug( + "ZSTD decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)", + in_bytes, + max_bytes - left, + (double) (max_bytes - left) / in_bytes * 100); + return 0; +#else + log_debug("Cannot decompress file. Compiled without ZSTD support."); + return -EPROTONOSUPPORT; +#endif +} + int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { if (endswith(filename, ".lz4")) return decompress_stream_lz4(fdf, fdt, max_bytes); else if (endswith(filename, ".xz")) return decompress_stream_xz(fdf, fdt, max_bytes); + else if (endswith(filename, ".zst")) + return decompress_stream_zstd(fdf, fdt, max_bytes); else return -EPROTONOSUPPORT; } diff --git a/src/journal/compress.h b/src/journal/compress.h index 56411484c..ab44ff06e 100644 --- a/src/journal/compress.h +++ b/src/journal/compress.h @@ -12,18 +12,26 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size); int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size); +int compress_blob_zstd(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size); static inline int compress_blob(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size) { int r; -#if HAVE_LZ4 +#if HAVE_ZSTD + r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size); + if (r == 0) + return OBJECT_COMPRESSED_ZSTD; +#elif HAVE_LZ4 r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size); if (r == 0) return OBJECT_COMPRESSED_LZ4; -#else +#elif HAVE_XZ r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size); if (r == 0) return OBJECT_COMPRESSED_XZ; +#else + r = -EOPNOTSUPP; #endif return r; } @@ -32,6 +40,8 @@ int decompress_blob_xz(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); int decompress_blob_lz4(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +int decompress_blob_zstd(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); int decompress_blob(int compression, const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); @@ -44,6 +54,10 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, void **buffer, size_t *buffer_size, const void *prefix, size_t prefix_len, uint8_t extra); +int decompress_startswith_zstd(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); int decompress_startswith(int compression, const void *src, uint64_t src_size, void **buffer, size_t *buffer_size, @@ -52,16 +66,26 @@ int decompress_startswith(int compression, int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes); int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes); +int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes); int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); +int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size); -#if HAVE_LZ4 +#if HAVE_ZSTD +# define compress_stream compress_stream_zstd +# define COMPRESSED_EXT ".zst" +#elif HAVE_LZ4 # define compress_stream compress_stream_lz4 # define COMPRESSED_EXT ".lz4" -#else +#elif HAVE_XZ # define compress_stream compress_stream_xz # define COMPRESSED_EXT ".xz" +#else +static inline int compress_stream(int fdf, int fdt, uint64_t max_size) { + return -EOPNOTSUPP; +} +# define COMPRESSED_EXT "" #endif int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes); diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index ff4e71a31..e9ddbb9da 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -9,7 +9,7 @@ /* * If you change this file you probably should also change its documentation: * - * http://www.freedesktop.org/wiki/Software/systemd/journal-files + * https://systemd.io/JOURNAL_FILE_FORMAT */ typedef struct Header Header; @@ -44,13 +44,13 @@ typedef enum ObjectType { /* Object flags */ enum { - OBJECT_COMPRESSED_XZ = 1 << 0, - OBJECT_COMPRESSED_LZ4 = 1 << 1, - _OBJECT_COMPRESSED_MAX + OBJECT_COMPRESSED_XZ = 1 << 0, + OBJECT_COMPRESSED_LZ4 = 1 << 1, + OBJECT_COMPRESSED_ZSTD = 1 << 2, + OBJECT_COMPRESSION_MASK = (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4 | OBJECT_COMPRESSED_ZSTD), + _OBJECT_COMPRESSED_MAX = OBJECT_COMPRESSION_MASK, }; -#define OBJECT_COMPRESSION_MASK (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4) - struct ObjectHeader { uint8_t type; uint8_t flags; @@ -74,13 +74,17 @@ struct DataObject DataObject__contents; struct DataObject__packed DataObject__contents _packed_; assert_cc(sizeof(struct DataObject) == sizeof(struct DataObject__packed)); -struct FieldObject { - ObjectHeader object; - le64_t hash; - le64_t next_hash_offset; - le64_t head_data_offset; - uint8_t payload[]; -} _packed_; +#define FieldObject__contents { \ + ObjectHeader object; \ + le64_t hash; \ + le64_t next_hash_offset; \ + le64_t head_data_offset; \ + uint8_t payload[]; \ +} + +struct FieldObject FieldObject__contents; +struct FieldObject__packed FieldObject__contents _packed_; +assert_cc(sizeof(struct FieldObject) == sizeof(struct FieldObject__packed)); struct EntryItem { le64_t object_offset; @@ -145,24 +149,38 @@ enum { /* Header flags */ enum { - HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, - HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, + HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, + HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, + HEADER_INCOMPATIBLE_KEYED_HASH = 1 << 2, + HEADER_INCOMPATIBLE_COMPRESSED_ZSTD = 1 << 3, }; -#define HEADER_INCOMPATIBLE_ANY (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4) +#define HEADER_INCOMPATIBLE_ANY \ + (HEADER_INCOMPATIBLE_COMPRESSED_XZ | \ + HEADER_INCOMPATIBLE_COMPRESSED_LZ4 | \ + HEADER_INCOMPATIBLE_KEYED_HASH | \ + HEADER_INCOMPATIBLE_COMPRESSED_ZSTD) -#if HAVE_XZ && HAVE_LZ4 +#if HAVE_XZ && HAVE_LZ4 && HAVE_ZSTD # define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_ANY +#elif HAVE_XZ && HAVE_LZ4 +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_KEYED_HASH) +#elif HAVE_XZ && HAVE_ZSTD +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH) +#elif HAVE_LZ4 && HAVE_ZSTD +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH) #elif HAVE_XZ -# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_XZ +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_KEYED_HASH) #elif HAVE_LZ4 -# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_LZ4 +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_KEYED_HASH) +#elif HAVE_ZSTD +# define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH) #else -# define HEADER_INCOMPATIBLE_SUPPORTED 0 +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_KEYED_HASH #endif enum { - HEADER_COMPATIBLE_SEALED = 1 + HEADER_COMPATIBLE_SEALED = 1 << 0, }; #define HEADER_COMPATIBLE_ANY HEADER_COMPATIBLE_SEALED @@ -172,7 +190,8 @@ enum { # define HEADER_COMPATIBLE_SUPPORTED 0 #endif -#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) +#define HEADER_SIGNATURE \ + ((const char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) #define struct_Header__contents { \ uint8_t signature[8]; /* "LPKSHHRH" */ \ @@ -205,14 +224,18 @@ enum { /* Added in 189 */ \ le64_t n_tags; \ le64_t n_entry_arrays; \ + /* Added in 246 */ \ + le64_t data_hash_chain_depth; \ + le64_t field_hash_chain_depth; \ } struct Header struct_Header__contents; struct Header__packed struct_Header__contents _packed_; assert_cc(sizeof(struct Header) == sizeof(struct Header__packed)); -assert_cc(sizeof(struct Header) == 240); +assert_cc(sizeof(struct Header) == 256); -#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) +#define FSS_HEADER_SIGNATURE \ + ((const char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) struct FSSHeader { uint8_t signature[8]; /* "KSHHRHLP" */ diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index bd5363586..cdcded2e2 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -16,6 +16,7 @@ #include "btrfs-util.h" #include "chattr-util.h" #include "compress.h" +#include "env-util.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" @@ -81,6 +82,9 @@ /* The mmap context to use for the header we pick as one above the last defined typed */ #define CONTEXT_HEADER _OBJECT_TYPE_MAX +/* Longest hash chain to rotate after */ +#define HASH_CHAIN_DEPTH_MAX 100 + #ifdef __clang__ # pragma GCC diagnostic ignored "-Waddress-of-packed-member" #endif @@ -388,7 +392,7 @@ JournalFile* journal_file_close(JournalFile *f) { ordered_hashmap_free_free(f->chain_cache); -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION free(f->compress_buffer); #endif @@ -419,7 +423,9 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { h.incompatible_flags |= htole32( f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ | - f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4); + f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4 | + f->compress_zstd * HEADER_INCOMPATIBLE_COMPRESSED_ZSTD | + f->keyed_hash * HEADER_INCOMPATIBLE_KEYED_HASH); h.compatible_flags = htole32( f->seal * HEADER_COMPATIBLE_SEALED); @@ -445,7 +451,6 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { } static int journal_file_refresh_header(JournalFile *f) { - sd_id128_t boot_id; int r; assert(f); @@ -458,12 +463,10 @@ static int journal_file_refresh_header(JournalFile *f) { else if (r < 0) return r; - r = sd_id128_get_boot(&boot_id); + r = sd_id128_get_boot(&f->header->boot_id); if (r < 0) return r; - f->header->boot_id = boot_id; - r = journal_file_set_online(f); /* Sync the online state to disk */ @@ -489,16 +492,23 @@ static bool warn_wrong_flags(const JournalFile *f, bool compatible) { f->path, type, flags & ~any); flags = (flags & any) & ~supported; if (flags) { - const char* strv[3]; + const char* strv[5]; unsigned n = 0; _cleanup_free_ char *t = NULL; - if (compatible && (flags & HEADER_COMPATIBLE_SEALED)) - strv[n++] = "sealed"; - if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) - strv[n++] = "xz-compressed"; - if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) - strv[n++] = "lz4-compressed"; + if (compatible) { + if (flags & HEADER_COMPATIBLE_SEALED) + strv[n++] = "sealed"; + } else { + if (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ) + strv[n++] = "xz-compressed"; + if (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4) + strv[n++] = "lz4-compressed"; + if (flags & HEADER_INCOMPATIBLE_COMPRESSED_ZSTD) + strv[n++] = "zstd-compressed"; + if (flags & HEADER_INCOMPATIBLE_KEYED_HASH) + strv[n++] = "keyed-hash"; + } strv[n] = NULL; assert(n < ELEMENTSOF(strv)); @@ -533,7 +543,7 @@ static int journal_file_verify_header(JournalFile *f) { if (f->header->state >= _STATE_MAX) return -EBADMSG; - header_size = le64toh(f->header->header_size); + header_size = le64toh(READ_NOW(f->header->header_size)); /* The first addition was n_data, so check that we are at least this large */ if (header_size < HEADER_SIZE_MIN) @@ -542,7 +552,7 @@ static int journal_file_verify_header(JournalFile *f) { if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) return -EBADMSG; - arena_size = le64toh(f->header->arena_size); + arena_size = le64toh(READ_NOW(f->header->arena_size)); if (UINT64_MAX - header_size < arena_size || header_size + arena_size > (uint64_t) f->last_stat.st_size) return -ENODATA; @@ -595,9 +605,12 @@ static int journal_file_verify_header(JournalFile *f) { f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header); f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header); + f->compress_zstd = JOURNAL_HEADER_COMPRESSED_ZSTD(f->header); f->seal = JOURNAL_HEADER_SEALED(f->header); + f->keyed_hash = JOURNAL_HEADER_KEYED_HASH(f->header); + return 0; } @@ -612,7 +625,7 @@ int journal_file_fstat(JournalFile *f) { f->last_stat_usec = now(CLOCK_MONOTONIC); - /* Refuse dealing with with files that aren't regular */ + /* Refuse dealing with files that aren't regular */ r = stat_verify_regular(&f->last_stat); if (r < 0) return r; @@ -625,26 +638,29 @@ int journal_file_fstat(JournalFile *f) { } static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { - uint64_t old_size, new_size; + uint64_t old_size, new_size, old_header_size, old_arena_size; int r; assert(f); assert(f->header); - /* We assume that this file is not sparse, and we know that - * for sure, since we always call posix_fallocate() - * ourselves */ + /* We assume that this file is not sparse, and we know that for sure, since we always call + * posix_fallocate() ourselves */ + + if (size > PAGE_ALIGN_DOWN(UINT64_MAX) - offset) + return -EINVAL; if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) return -EIO; - old_size = - le64toh(f->header->header_size) + - le64toh(f->header->arena_size); + old_header_size = le64toh(READ_NOW(f->header->header_size)); + old_arena_size = le64toh(READ_NOW(f->header->arena_size)); + if (old_arena_size > PAGE_ALIGN_DOWN(UINT64_MAX) - old_header_size) + return -EBADMSG; - new_size = PAGE_ALIGN(offset + size); - if (new_size < le64toh(f->header->header_size)) - new_size = le64toh(f->header->header_size); + old_size = old_header_size + old_arena_size; + + new_size = MAX(PAGE_ALIGN(offset + size), old_header_size); if (new_size <= old_size) { @@ -690,7 +706,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) if (r != 0) return -r; - f->header->arena_size = htole64(new_size - le64toh(f->header->header_size)); + f->header->arena_size = htole64(new_size - old_header_size); return journal_file_fstat(f); } @@ -702,7 +718,15 @@ static unsigned type_to_context(ObjectType type) { return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0; } -static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret, size_t *ret_size) { +static int journal_file_move_to( + JournalFile *f, + ObjectType type, + bool keep_always, + uint64_t offset, + uint64_t size, + void **ret, + size_t *ret_size) { + int r; assert(f); @@ -711,6 +735,9 @@ static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_alway if (size <= 0) return -EINVAL; + if (size > UINT64_MAX - offset) + return -EBADMSG; + /* Avoid SIGBUS on invalid accesses */ if (offset + size > (uint64_t) f->last_stat.st_size) { /* Hmm, out of range? Let's refresh the fstat() data @@ -760,7 +787,7 @@ 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) <= 0) + if (le64toh(o->object.size) <= offsetof(DataObject, payload)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64, offsetof(DataObject, payload), @@ -782,7 +809,7 @@ 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) <= 0) + if (le64toh(o->object.size) <= offsetof(FieldObject, payload)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64, offsetof(FieldObject, payload), @@ -798,18 +825,22 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) offset); break; - case OBJECT_ENTRY: - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) + case OBJECT_ENTRY: { + uint64_t sz; + + sz = le64toh(READ_NOW(o->object.size)); + if (sz < offsetof(EntryObject, items) || + (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64, offsetof(EntryObject, items), - le64toh(o->object.size), + sz, offset); - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) + if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid number items in entry: %" PRIu64 ": %" PRIu64, - (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem), + (sz - offsetof(EntryObject, items)) / sizeof(EntryItem), offset); if (le64toh(o->entry.seqnum) <= 0) @@ -831,25 +862,35 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) offset); break; + } 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) + case OBJECT_FIELD_HASH_TABLE: { + 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) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid %s hash table size: %" PRIu64 ": %" PRIu64, o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - le64toh(o->object.size), + sz, offset); 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) + case OBJECT_ENTRY_ARRAY: { + 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) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid object entry array size: %" PRIu64 ": %" PRIu64, - le64toh(o->object.size), + sz, offset); if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) @@ -859,6 +900,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) offset); break; + } case OBJECT_TAG: if (le64toh(o->object.size) != sizeof(TagObject)) @@ -905,7 +947,7 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset return r; o = (Object*) t; - s = le64toh(o->object.size); + s = le64toh(READ_NOW(o->object.size)); if (s == 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), @@ -974,7 +1016,13 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { return r; } -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 r; uint64_t p; Object *tail, *o; @@ -984,8 +1032,6 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O assert(f->header); assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX); assert(size >= sizeof(ObjectHeader)); - assert(offset); - assert(ret); r = journal_file_set_online(f); if (r < 0) @@ -995,11 +1041,21 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O if (p == 0) p = le64toh(f->header->header_size); else { + uint64_t sz; + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); if (r < 0) return r; - p += ALIGN64(le64toh(tail->object.size)); + 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; } r = journal_file_allocate(f, p, size); @@ -1011,16 +1067,19 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O return r; o = (Object*) t; - - zero(o->object); - o->object.type = type; - o->object.size = htole64(size); + o->object = (ObjectHeader) { + .type = type, + .size = htole64(size), + }; f->header->tail_object_offset = htole64(p); f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); - *ret = o; - *offset = p; + if (ret) + *ret = o; + + if (ret_offset) + *ret_offset = p; return 0; } @@ -1042,7 +1101,7 @@ static int journal_file_setup_data_hash_table(JournalFile *f) { if (s < DEFAULT_DATA_HASH_TABLE_SIZE) s = DEFAULT_DATA_HASH_TABLE_SIZE; - log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem)); + log_debug("Reserving %"PRIu64" entries in data hash table.", s / sizeof(HashItem)); r = journal_file_append_object(f, OBJECT_DATA_HASH_TABLE, @@ -1071,6 +1130,8 @@ static int journal_file_setup_field_hash_table(JournalFile *f) { * number should grow very slowly only */ s = DEFAULT_FIELD_HASH_TABLE_SIZE; + log_debug("Reserving %"PRIu64" entries in field hash table.", s / sizeof(HashItem)); + r = journal_file_append_object(f, OBJECT_FIELD_HASH_TABLE, offsetof(Object, hash_table.items) + s, @@ -1156,7 +1217,7 @@ static int journal_file_link_field( if (o->object.type != OBJECT_FIELD) return -EINVAL; - m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem); if (m <= 0) return -EBADMSG; @@ -1201,7 +1262,7 @@ static int journal_file_link_data( if (o->object.type != OBJECT_DATA) return -EINVAL; - m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem); if (m <= 0) return -EBADMSG; @@ -1234,12 +1295,38 @@ static int journal_file_link_data( return 0; } +static int next_hash_offset( + JournalFile *f, + uint64_t *p, + le64_t *next_hash_offset, + uint64_t *depth, + le64_t *header_max_depth) { + + uint64_t nextp; + + nextp = le64toh(READ_NOW(*next_hash_offset)); + if (nextp > 0) { + if (nextp <= *p) /* Refuse going in loops */ + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Detected hash item loop in %s, refusing.", f->path); + + (*depth)++; + + /* If the depth of this hash chain is larger than all others we have seen so far, record it */ + if (header_max_depth && f->writable) + *header_max_depth = htole64(MAX(*depth, le64toh(*header_max_depth))); + } + + *p = nextp; + return 0; +} + int journal_file_find_field_object_with_hash( JournalFile *f, const void *field, uint64_t size, uint64_t hash, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { - uint64_t p, osize, h, m; + uint64_t p, osize, h, m, depth = 0; int r; assert(f); @@ -1257,13 +1344,12 @@ int journal_file_find_field_object_with_hash( osize = offsetof(Object, field.payload) + size; - m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem); if (m <= 0) return -EBADMSG; h = hash % m; p = le64toh(f->field_hash_table[h].head_hash_offset); - while (p > 0) { Object *o; @@ -1277,41 +1363,63 @@ int journal_file_find_field_object_with_hash( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 1; } - p = le64toh(o->field.next_hash_offset); + r = next_hash_offset( + f, + &p, + &o->field.next_hash_offset, + &depth, + JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) ? &f->header->field_hash_chain_depth : NULL); + if (r < 0) + return r; } return 0; } +uint64_t journal_file_hash_data( + JournalFile *f, + const void *data, + size_t sz) { + + assert(f); + assert(data || sz == 0); + + /* We try to unify our codebase on siphash, hence new-styled journal files utilizing the keyed hash + * function use siphash. Old journal files use the Jenkins hash. */ + + if (JOURNAL_HEADER_KEYED_HASH(f->header)) + return siphash24(data, sz, f->header->file_id.bytes); + + return jenkins_hash64(data, sz); +} + int journal_file_find_field_object( JournalFile *f, const void *field, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash; + Object **ret, uint64_t *ret_offset) { assert(f); assert(field && size > 0); - hash = hash64(field, size); - - return journal_file_find_field_object_with_hash(f, - field, size, hash, - ret, offset); + return journal_file_find_field_object_with_hash( + f, + field, size, + journal_file_hash_data(f, field, size), + ret, 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 *offset) { + Object **ret, uint64_t *ret_offset) { - uint64_t p, osize, h, m; + uint64_t p, osize, h, m, depth = 0; int r; assert(f); @@ -1329,7 +1437,7 @@ int journal_file_find_data_object_with_hash( osize = offsetof(Object, data.payload) + size; - m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem); if (m <= 0) return -EBADMSG; @@ -1347,11 +1455,11 @@ int journal_file_find_data_object_with_hash( goto next; if (o->object.flags & OBJECT_COMPRESSION_MASK) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION uint64_t l; size_t rsize = 0; - l = le64toh(o->object.size); + l = le64toh(READ_NOW(o->object.size)); if (l <= offsetof(Object, data.payload)) return -EBADMSG; @@ -1368,8 +1476,8 @@ int journal_file_find_data_object_with_hash( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 1; } @@ -1382,14 +1490,21 @@ int journal_file_find_data_object_with_hash( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 1; } next: - p = le64toh(o->data.next_hash_offset); + r = next_hash_offset( + f, + &p, + &o->data.next_hash_offset, + &depth, + JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) ? &f->header->data_hash_chain_depth : NULL); + if (r < 0) + return r; } return 0; @@ -1398,24 +1513,22 @@ int journal_file_find_data_object_with_hash( int journal_file_find_data_object( JournalFile *f, const void *data, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash; + Object **ret, uint64_t *ret_offset) { assert(f); assert(data || size == 0); - hash = hash64(data, size); - - return journal_file_find_data_object_with_hash(f, - data, size, hash, - ret, offset); + return journal_file_find_data_object_with_hash( + f, + data, size, + journal_file_hash_data(f, data, size), + ret, ret_offset); } static int journal_file_append_field( JournalFile *f, const void *field, uint64_t size, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { uint64_t hash, p; uint64_t osize; @@ -1425,7 +1538,7 @@ static int journal_file_append_field( assert(f); assert(field && size > 0); - hash = hash64(field, size); + hash = journal_file_hash_data(f, field, size); r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p); if (r < 0) @@ -1435,8 +1548,8 @@ static int journal_file_append_field( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 0; } @@ -1468,8 +1581,8 @@ static int journal_file_append_field( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 0; } @@ -1477,7 +1590,7 @@ static int journal_file_append_field( static int journal_file_append_data( JournalFile *f, const void *data, uint64_t size, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { uint64_t hash, p; uint64_t osize; @@ -1488,7 +1601,7 @@ static int journal_file_append_data( assert(f); assert(data || size == 0); - hash = hash64(data, size); + hash = journal_file_hash_data(f, data, size); r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); if (r < 0) @@ -1498,8 +1611,8 @@ static int journal_file_append_data( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 0; } @@ -1511,7 +1624,7 @@ static int journal_file_append_data( o->data.hash = htole64(hash); -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION if (JOURNAL_FILE_COMPRESS(f) && size >= f->compress_threshold_bytes) { size_t rsize = 0; @@ -1569,37 +1682,54 @@ static int journal_file_append_data( if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 0; } uint64_t journal_file_entry_n_items(Object *o) { + uint64_t sz; assert(o); if (o->object.type != OBJECT_ENTRY) return 0; - return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); + sz = le64toh(READ_NOW(o->object.size)); + if (sz < offsetof(Object, entry.items)) + return 0; + + return (sz - offsetof(Object, entry.items)) / sizeof(EntryItem); } uint64_t journal_file_entry_array_n_items(Object *o) { + uint64_t sz; + assert(o); if (o->object.type != OBJECT_ENTRY_ARRAY) return 0; - return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); + sz = le64toh(READ_NOW(o->object.size)); + if (sz < offsetof(Object, entry_array.items)) + return 0; + + return (sz - offsetof(Object, entry_array.items)) / sizeof(uint64_t); } uint64_t journal_file_hash_table_n_items(Object *o) { + uint64_t sz; + assert(o); if (!IN_SET(o->object.type, OBJECT_DATA_HASH_TABLE, OBJECT_FIELD_HASH_TABLE)) return 0; - return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem); + sz = le64toh(READ_NOW(o->object.size)); + if (sz < offsetof(Object, hash_table.items)) + return 0; + + return (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem); } static int link_entry_into_array(JournalFile *f, @@ -1617,7 +1747,7 @@ static int link_entry_into_array(JournalFile *f, assert(p > 0); a = le64toh(*first); - i = hidx = le64toh(*idx); + i = hidx = le64toh(READ_NOW(*idx)); while (a > 0) { r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); @@ -1682,6 +1812,7 @@ static int link_entry_into_array_plus_one(JournalFile *f, le64_t *idx, uint64_t p) { + uint64_t hidx; int r; assert(f); @@ -1690,32 +1821,33 @@ static int link_entry_into_array_plus_one(JournalFile *f, assert(idx); assert(p > 0); - if (*idx == 0) + hidx = le64toh(READ_NOW(*idx)); + if (hidx == UINT64_MAX) + return -EBADMSG; + if (hidx == 0) *extra = htole64(p); else { le64_t i; - i = htole64(le64toh(*idx) - 1); + i = htole64(hidx - 1); r = link_entry_into_array(f, first, &i, p); if (r < 0) return r; } - *idx = htole64(le64toh(*idx) + 1); + *idx = htole64(hidx + 1); return 0; } static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { uint64_t p; int r; + assert(f); assert(o); assert(offset > 0); p = le64toh(o->entry.items[i].object_offset); - if (p == 0) - return -EINVAL; - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; @@ -1775,7 +1907,7 @@ static int journal_file_append_entry_internal( uint64_t xor_hash, const EntryItem items[], unsigned n_items, uint64_t *seqnum, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { uint64_t np; uint64_t osize; Object *o; @@ -1814,8 +1946,8 @@ static int journal_file_append_entry_internal( if (ret) *ret = o; - if (offset) - *offset = np; + if (ret_offset) + *ret_offset = np; return 0; } @@ -1919,7 +2051,7 @@ int journal_file_append_entry( const sd_id128_t *boot_id, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { unsigned i; EntryItem *items; @@ -1962,7 +2094,20 @@ int journal_file_append_entry( if (r < 0) return r; - xor_hash ^= le64toh(o->data.hash); + /* When calculating the XOR hash field, we need to take special care if the "keyed-hash" + * journal file flag is on. We use the XOR hash field to quickly determine the identity of a + * specific record, and give records with otherwise identical position (i.e. match in seqno, + * timestamp, …) a stable ordering. But for that we can't have it that the hash of the + * objects in each file is different since they are keyed. Hence let's calculate the Jenkins + * hash here for that. This also has the benefit that cursors for old and new journal files + * are completely identical (they include the XOR hash after all). For classic Jenkins-hash + * files things are easier, we can just take the value from the stored record directly. */ + + if (JOURNAL_HEADER_KEYED_HASH(f->header)) + xor_hash ^= jenkins_hash64(iovec[i].iov_base, iovec[i].iov_len); + else + xor_hash ^= le64toh(o->data.hash); + items[i].object_offset = htole64(p); items[i].hash = o->data.hash; } @@ -1971,7 +2116,7 @@ int journal_file_append_entry( * times for rotating media. */ typesafe_qsort(items, n_iovec, entry_item_cmp); - r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, offset); + r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, ret_offset); /* If the memory mapping triggered a SIGBUS then we return an * IO error and ignore the error code passed down to us, since @@ -2040,7 +2185,7 @@ static int generic_array_get( JournalFile *f, uint64_t first, uint64_t i, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { Object *o; uint64_t p = 0, a, t = 0; @@ -2090,8 +2235,8 @@ found: if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; return 1; } @@ -2101,7 +2246,7 @@ static int generic_array_get_plus_one( uint64_t extra, uint64_t first, uint64_t i, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { Object *o; @@ -2117,13 +2262,13 @@ static int generic_array_get_plus_one( if (ret) *ret = o; - if (offset) - *offset = extra; + if (ret_offset) + *ret_offset = extra; return 1; } - return generic_array_get(f, first, i-1, ret, offset); + return generic_array_get(f, first, i-1, ret, ret_offset); } enum { @@ -2140,8 +2285,8 @@ static int generic_array_bisect( int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), direction_t direction, Object **ret, - uint64_t *offset, - uint64_t *idx) { + uint64_t *ret_offset, + uint64_t *ret_idx) { uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1; bool subtract_one = false; @@ -2340,11 +2485,11 @@ found: if (ret) *ret = o; - if (offset) - *offset = p; + if (ret_offset) + *ret_offset = p; - if (idx) - *idx = t + i + (subtract_one ? -1 : 0); + if (ret_idx) + *ret_idx = t + i + (subtract_one ? -1 : 0); return 1; } @@ -2358,8 +2503,8 @@ static int generic_array_bisect_plus_one( int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), direction_t direction, Object **ret, - uint64_t *offset, - uint64_t *idx) { + uint64_t *ret_offset, + uint64_t *ret_idx) { int r; bool step_back = false; @@ -2395,13 +2540,13 @@ static int generic_array_bisect_plus_one( return 0; } - r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); + r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, ret_offset, ret_idx); if (r == 0 && step_back) goto found; - if (r > 0 && idx) - (*idx)++; + if (r > 0 && ret_idx) + (*ret_idx)++; return r; @@ -2413,11 +2558,11 @@ found: if (ret) *ret = o; - if (offset) - *offset = extra; + if (ret_offset) + *ret_offset = extra; - if (idx) - *idx = 0; + if (ret_idx) + *ret_idx = 0; return 1; } @@ -2435,6 +2580,7 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle } static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + uint64_t sq; Object *o; int r; @@ -2445,9 +2591,10 @@ static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { if (r < 0) return r; - if (le64toh(o->entry.seqnum) == needle) + sq = le64toh(READ_NOW(o->entry.seqnum)); + if (sq == needle) return TEST_FOUND; - else if (le64toh(o->entry.seqnum) < needle) + else if (sq < needle) return TEST_LEFT; else return TEST_RIGHT; @@ -2458,21 +2605,23 @@ int journal_file_move_to_entry_by_seqnum( uint64_t seqnum, direction_t direction, Object **ret, - uint64_t *offset) { + 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), - seqnum, - test_object_seqnum, - direction, - ret, offset, NULL); + return generic_array_bisect( + f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + seqnum, + test_object_seqnum, + direction, + ret, ret_offset, NULL); } static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { Object *o; + uint64_t rt; int r; assert(f); @@ -2482,9 +2631,10 @@ static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { if (r < 0) return r; - if (le64toh(o->entry.realtime) == needle) + rt = le64toh(READ_NOW(o->entry.realtime)); + if (rt == needle) return TEST_FOUND; - else if (le64toh(o->entry.realtime) < needle) + else if (rt < needle) return TEST_LEFT; else return TEST_RIGHT; @@ -2495,21 +2645,23 @@ int journal_file_move_to_entry_by_realtime( uint64_t realtime, direction_t direction, Object **ret, - uint64_t *offset) { + 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), - realtime, - test_object_realtime, - direction, - ret, offset, NULL); + return generic_array_bisect( + f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + realtime, + test_object_realtime, + direction, + ret, ret_offset, NULL); } static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { Object *o; + uint64_t m; int r; assert(f); @@ -2519,9 +2671,10 @@ static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { if (r < 0) return r; - if (le64toh(o->entry.monotonic) == needle) + m = le64toh(READ_NOW(o->entry.monotonic)); + if (m == needle) return TEST_FOUND; - else if (le64toh(o->entry.monotonic) < needle) + else if (m < needle) return TEST_LEFT; else return TEST_RIGHT; @@ -2545,7 +2698,7 @@ int journal_file_move_to_entry_by_monotonic( uint64_t monotonic, direction_t direction, Object **ret, - uint64_t *offset) { + uint64_t *ret_offset) { Object *o; int r; @@ -2558,14 +2711,15 @@ int journal_file_move_to_entry_by_monotonic( if (r == 0) return -ENOENT; - return generic_array_bisect_plus_one(f, - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - le64toh(o->data.n_entries), - monotonic, - test_object_monotonic, - direction, - ret, offset, NULL); + return generic_array_bisect_plus_one( + f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + ret, ret_offset, NULL); } void journal_file_reset_location(JournalFile *f) { @@ -2671,7 +2825,7 @@ int journal_file_next_entry( JournalFile *f, uint64_t p, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { uint64_t i, n, ofs; int r; @@ -2679,7 +2833,7 @@ int journal_file_next_entry( assert(f); assert(f->header); - n = le64toh(f->header->n_entries); + n = le64toh(READ_NOW(f->header->n_entries)); if (n <= 0) return 0; @@ -2728,8 +2882,8 @@ int journal_file_next_entry( "%s: entry array not properly ordered at entry %" PRIu64, f->path, i); - if (offset) - *offset = ofs; + if (ret_offset) + *ret_offset = ofs; return 1; } @@ -2739,7 +2893,7 @@ int journal_file_next_entry_for_data( Object *o, uint64_t p, uint64_t data_offset, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { uint64_t i, n, ofs; Object *d; @@ -2752,7 +2906,7 @@ int journal_file_next_entry_for_data( if (r < 0) return r; - n = le64toh(d->data.n_entries); + n = le64toh(READ_NOW(d->data.n_entries)); if (n <= 0) return n; @@ -2804,8 +2958,8 @@ int journal_file_next_entry_for_data( "%s data entry array not properly ordered at entry %" PRIu64, f->path, i); - if (offset) - *offset = ofs; + if (ret_offset) + *ret_offset = ofs; return 1; } @@ -2815,7 +2969,7 @@ int journal_file_move_to_entry_by_offset_for_data( uint64_t data_offset, uint64_t p, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { int r; Object *d; @@ -2826,14 +2980,15 @@ int journal_file_move_to_entry_by_offset_for_data( if (r < 0) return r; - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - p, - test_object_offset, - direction, - ret, offset, NULL); + return generic_array_bisect_plus_one( + f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + direction, + ret, ret_offset, NULL); } int journal_file_move_to_entry_by_monotonic_for_data( @@ -2842,7 +2997,7 @@ int journal_file_move_to_entry_by_monotonic_for_data( sd_id128_t boot_id, uint64_t monotonic, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { Object *o, *d; int r; @@ -2909,8 +3064,8 @@ int journal_file_move_to_entry_by_monotonic_for_data( if (p == q) { if (ret) *ret = qo; - if (offset) - *offset = q; + if (ret_offset) + *ret_offset = q; return 1; } @@ -2924,7 +3079,7 @@ int journal_file_move_to_entry_by_seqnum_for_data( uint64_t data_offset, uint64_t seqnum, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { Object *d; int r; @@ -2935,14 +3090,15 @@ int journal_file_move_to_entry_by_seqnum_for_data( if (r < 0) return r; - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - seqnum, - test_object_seqnum, - direction, - ret, offset, NULL); + return generic_array_bisect_plus_one( + f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + seqnum, + test_object_seqnum, + direction, + ret, ret_offset, NULL); } int journal_file_move_to_entry_by_realtime_for_data( @@ -2950,7 +3106,7 @@ int journal_file_move_to_entry_by_realtime_for_data( uint64_t data_offset, uint64_t realtime, direction_t direction, - Object **ret, uint64_t *offset) { + Object **ret, uint64_t *ret_offset) { Object *d; int r; @@ -2961,14 +3117,15 @@ int journal_file_move_to_entry_by_realtime_for_data( if (r < 0) return r; - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - realtime, - test_object_realtime, - direction, - ret, offset, NULL); + return generic_array_bisect_plus_one( + f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + realtime, + test_object_realtime, + direction, + ret, ret_offset, NULL); } void journal_file_dump(JournalFile *f) { @@ -2981,7 +3138,7 @@ void journal_file_dump(JournalFile *f) { journal_file_print_header(f); - p = le64toh(f->header->header_size); + p = le64toh(READ_NOW(f->header->header_size)); while (p != 0) { r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); if (r < 0) @@ -3038,7 +3195,7 @@ void journal_file_dump(JournalFile *f) { if (p == le64toh(f->header->tail_object_offset)) p = 0; else - p = p + ALIGN64(le64toh(o->object.size)); + p += ALIGN64(le64toh(o->object.size)); } return; @@ -3071,7 +3228,7 @@ void journal_file_print_header(JournalFile *f) { "Sequential number ID: %s\n" "State: %s\n" "Compatible flags:%s%s\n" - "Incompatible flags:%s%s%s\n" + "Incompatible flags:%s%s%s%s%s\n" "Header size: %"PRIu64"\n" "Arena size: %"PRIu64"\n" "Data hash table size: %"PRIu64"\n" @@ -3096,6 +3253,8 @@ void journal_file_print_header(JournalFile *f) { (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "", JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "", JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "", + JOURNAL_HEADER_COMPRESSED_ZSTD(f->header) ? " COMPRESSED-ZSTD" : "", + JOURNAL_HEADER_KEYED_HASH(f->header) ? " KEYED-HASH" : "", (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "", le64toh(f->header->header_size), le64toh(f->header->arena_size), @@ -3129,6 +3288,14 @@ void journal_file_print_header(JournalFile *f) { printf("Entry array objects: %"PRIu64"\n", le64toh(f->header->n_entry_arrays)); + if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth)) + printf("Deepest field hash chain: %" PRIu64"\n", + f->header->field_hash_chain_depth); + + if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth)) + printf("Deepest data hash chain: %" PRIu64"\n", + f->header->data_hash_chain_depth); + if (fstat(f->fd, &st) >= 0) printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL)); } @@ -3208,7 +3375,9 @@ int journal_file_open( .prot = prot_from_flags(flags), .writable = (flags & O_ACCMODE) != O_RDONLY, -#if HAVE_LZ4 +#if HAVE_ZSTD + .compress_zstd = compress, +#elif HAVE_LZ4 .compress_lz4 = compress, #elif HAVE_XZ .compress_xz = compress, @@ -3221,19 +3390,31 @@ int journal_file_open( #endif }; + /* We turn on keyed hashes by default, but provide an environment variable to turn them off, if + * people really want that */ + 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."); + f->keyed_hash = true; + } else + f->keyed_hash = r; + if (DEBUG_LOGGING) { - static int last_seal = -1, last_compress = -1; + static int last_seal = -1, last_compress = -1, last_keyed_hash = -1; static uint64_t last_bytes = UINT64_MAX; char bytes[FORMAT_BYTES_MAX]; if (last_seal != f->seal || + last_keyed_hash != f->keyed_hash || last_compress != JOURNAL_FILE_COMPRESS(f) || last_bytes != f->compress_threshold_bytes) { - log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s", - yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)), + log_debug("Journal effective settings seal=%s keyed_hash=%s compress=%s compress_threshold_bytes=%s", + yes_no(f->seal), yes_no(f->keyed_hash), yes_no(JOURNAL_FILE_COMPRESS(f)), format_bytes(bytes, sizeof bytes, f->compress_threshold_bytes)); last_seal = f->seal; + last_keyed_hash = f->keyed_hash; last_compress = JOURNAL_FILE_COMPRESS(f); last_bytes = f->compress_threshold_bytes; } @@ -3659,7 +3840,11 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 if (le_hash != o->data.hash) return -EBADMSG; - l = le64toh(o->object.size) - offsetof(Object, data.payload); + l = le64toh(READ_NOW(o->object.size)); + if (l < offsetof(Object, data.payload)) + return -EBADMSG; + + l -= offsetof(Object, data.payload); t = (size_t) l; /* We hit the limit on 32bit machines */ @@ -3667,7 +3852,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 return -E2BIG; if (o->object.flags & OBJECT_COMPRESSION_MASK) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION size_t rsize = 0; r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, @@ -3687,7 +3872,11 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 if (r < 0) return r; - xor_hash ^= le64toh(u->data.hash); + if (JOURNAL_HEADER_KEYED_HASH(to->header)) + xor_hash ^= jenkins_hash64(data, l); + else + xor_hash ^= le64toh(u->data.hash); + items[i].object_offset = htole64(h); items[i].hash = u->data.hash; @@ -3874,11 +4063,9 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { return true; } - /* Let's check if the hash tables grew over a certain fill - * level (75%, borrowing this value from Java's hash table - * implementation), and if so suggest a rotation. To calculate - * the fill level we need the n_data field, which only exists - * in newer versions. */ + /* Let's check if the hash tables grew over a certain fill level (75%, borrowing this value from + * Java's hash table implementation), and if so suggest a rotation. To calculate the fill level we + * need the n_data field, which only exists in newer versions. */ if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) { @@ -3902,6 +4089,22 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { return true; } + /* If there are too many hash collisions somebody is most likely playing games with us. Hence, if our + * longest chain is longer than some threshold, let's suggest rotation. */ + if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) && + le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) { + log_debug("Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.", + f->path, le64toh(f->header->data_hash_chain_depth)); + return true; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) && + le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) { + log_debug("Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.", + f->path, le64toh(f->header->field_hash_chain_depth)); + return true; + } + /* Are the data objects properly indexed by field objects? */ if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && JOURNAL_HEADER_CONTAINS(f->header, n_fields) && diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index cf0f7691f..f80bf5d26 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -67,10 +67,12 @@ typedef struct JournalFile { bool writable:1; bool compress_xz:1; bool compress_lz4:1; + bool compress_zstd:1; bool seal:1; bool defrag_on_close:1; bool close_fd:1; bool archive:1; + bool keyed_hash:1; direction_t last_direction; LocationType location_type; @@ -105,7 +107,7 @@ typedef struct JournalFile { unsigned last_seen_generation; uint64_t compress_threshold_bytes; -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION void *compress_buffer; size_t compress_buffer_size; #endif @@ -187,13 +189,19 @@ static inline bool VALID_EPOCH(uint64_t u) { (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) #define JOURNAL_HEADER_SEALED(h) \ - (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED)) + FLAGS_SET(le32toh((h)->compatible_flags), HEADER_COMPATIBLE_SEALED) #define JOURNAL_HEADER_COMPRESSED_XZ(h) \ - (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) + FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_XZ) #define JOURNAL_HEADER_COMPRESSED_LZ4(h) \ - (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) + FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_LZ4) + +#define JOURNAL_HEADER_COMPRESSED_ZSTD(h) \ + FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_ZSTD) + +#define JOURNAL_HEADER_KEYED_HASH(h) \ + 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); @@ -260,5 +268,7 @@ int journal_file_map_field_hash_table(JournalFile *f); static inline bool JOURNAL_FILE_COMPRESS(JournalFile *f) { assert(f); - return f->compress_xz || f->compress_lz4; + return f->compress_xz || f->compress_lz4 || f->compress_zstd; } + +uint64_t journal_file_hash_data(JournalFile *f, const void *data, size_t sz); diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 1454df602..d87b0a11e 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -32,7 +32,7 @@ struct Match { /* For concrete matches */ char *data; size_t size; - le64_t le_hash; + uint64_t hash; /* old-style jenkins hash. New-style siphash is different per file, hence won't be cached here */ /* For terms */ LIST_HEAD(Match, matches); @@ -41,10 +41,10 @@ struct Match { struct Location { LocationType type; - bool seqnum_set; - bool realtime_set; - bool monotonic_set; - bool xor_hash_set; + bool seqnum_set:1; + bool realtime_set:1; + bool monotonic_set:1; + bool xor_hash_set:1; uint64_t seqnum; sd_id128_t seqnum_id; @@ -127,3 +127,12 @@ void journal_print_header(sd_journal *j); #define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval) \ for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; ) + +/* All errors that we might encounter while extracting a field that are not real errors, + * but only mean that the field is too large or we don't support the compression. */ +static inline bool JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(int r) { + return IN_SET(abs(r), + ENOBUFS, /* Field or decompressed field too large */ + E2BIG, /* Field too large for pointer width */ + EPROTONOSUPPORT); /* Unsupported compression */ +} diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index 912ecef73..d51d03acd 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -15,6 +15,7 @@ #include "errno-util.h" #include "fd-util.h" #include "io-util.h" +#include "fileio.h" #include "memfd-util.h" #include "socket-util.h" #include "stdio-util.h" @@ -73,12 +74,12 @@ _public_ int sd_journal_print(int priority, const char *format, ...) { } _public_ int sd_journal_printv(int priority, const char *format, va_list ap) { - - /* FIXME: Instead of limiting things to LINE_MAX we could do a - C99 variable-length array on the stack here in a loop. */ - - char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1]; + char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1]; + char sbuf[LINE_MAX + 8] = "MESSAGE="; struct iovec iov[2]; + int len; + va_list aq; + char *buffer = sbuf; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); @@ -86,14 +87,25 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) { xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); - memcpy(buffer, "MESSAGE=", 8); - vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + va_copy(aq, ap); + len = vsnprintf(buffer + 8, LINE_MAX, format, aq); + va_end(aq); + + if (len >= (int)LONG_LINE_MAX - 8) + return -ENOBUFS; + + /* Allocate large buffer to accommodate big message */ + if (len >= LINE_MAX) { + buffer = alloca(len + 9); + memcpy(buffer, "MESSAGE=", 8); + assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len); + } /* Strip trailing whitespace, keep prefix whitespace. */ (void) strstrip(buffer); /* Suppress empty lines */ - if (isempty(buffer+8)) + if (isempty(buffer + 8)) return 0; iov[0] = IOVEC_MAKE_STRING(buffer); @@ -437,9 +449,13 @@ _public_ int sd_journal_print_with_location(int priority, const char *file, cons } _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { - char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1]; + char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1]; + char sbuf[LINE_MAX + 8] = "MESSAGE="; struct iovec iov[5]; char *f; + int len; + char *buffer = sbuf; + va_list aq; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); @@ -447,14 +463,25 @@ _public_ int sd_journal_printv_with_location(int priority, const char *file, con xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); - memcpy(buffer, "MESSAGE=", 8); - vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + va_copy(aq, ap); + len = vsnprintf(buffer + 8, LINE_MAX, format, aq); + va_end(aq); + + if (len >= (int)LONG_LINE_MAX - 8) + return -ENOBUFS; + + /* Allocate large buffer to accommodate big message */ + if (len >= LINE_MAX) { + buffer = alloca(len + 9); + memcpy(buffer, "MESSAGE=", 8); + assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len); + } /* Strip trailing whitespace, keep prefixing whitespace */ (void) strstrip(buffer); /* Suppress empty lines */ - if (isempty(buffer+8)) + if (isempty(buffer + 8)) return 0; /* func is initialized from __func__ which is not a macro, but diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 344b7b019..eddb8054b 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -163,9 +163,9 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o return r; } - h2 = hash64(b, b_size); + h2 = journal_file_hash_data(f, b, b_size); } else - h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + h2 = journal_file_hash_data(f, o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); if (h1 != h2) { error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2); @@ -925,9 +925,10 @@ int journal_file_verify( goto fail; } - if ((o->object.flags & OBJECT_COMPRESSED_XZ) && - (o->object.flags & OBJECT_COMPRESSED_LZ4)) { - error(p, "Objected with double compression"); + if (!!(o->object.flags & OBJECT_COMPRESSED_XZ) + + !!(o->object.flags & OBJECT_COMPRESSED_LZ4) + + !!(o->object.flags & OBJECT_COMPRESSED_ZSTD) > 1) { + error(p, "Object has multiple compression flags set"); r = -EINVAL; goto fail; } @@ -944,6 +945,12 @@ int journal_file_verify( goto fail; } + if ((o->object.flags & OBJECT_COMPRESSED_ZSTD) && !JOURNAL_HEADER_COMPRESSED_ZSTD(f->header)) { + error(p, "ZSTD compressed object in file without ZSTD compression"); + r = -EBADMSG; + goto fail; + } + switch (o->object.type) { case OBJECT_DATA: diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 25492dad1..8d4897b94 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -351,7 +351,7 @@ static int help(void) { " -p --priority=RANGE Show entries with the specified priority\n" " --facility=FACILITY... Show entries with the specified facilities\n" " -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n" - " --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n" + " --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n" " -e --pager-end Immediately jump to the end in the pager\n" " -f --follow Follow the journal\n" " -n --lines[=INTEGER] Number of journal entries to show\n" @@ -875,12 +875,7 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Bad --facility= argument \"%s\".", fac); - r = set_ensure_allocated(&arg_facilities, NULL); - if (r < 0) - return log_oom(); - - r = set_put(arg_facilities, INT_TO_PTR(num)); - if (r < 0) + if (set_ensure_put(&arg_facilities, NULL, INT_TO_PTR(num)) < 0) return log_oom(); } @@ -1783,7 +1778,6 @@ static int setup_keys(void) { int fd = -1, r; sd_id128_t machine, boot; char *p = NULL, *k = NULL; - struct FSSHeader h; uint64_t n; struct stat st; @@ -1873,15 +1867,17 @@ static int setup_keys(void) { if (r < 0) log_warning_errno(r, "Failed to set file attributes: %m"); - zero(h); + struct FSSHeader h = { + .machine_id = machine, + .boot_id = boot, + .header_size = htole64(sizeof(h)), + .start_usec = htole64(n * arg_interval), + .interval_usec = htole64(arg_interval), + .fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR), + .fsprg_state_size = htole64(state_size), + }; + memcpy(h.signature, "KSHHRHLP", 8); - h.machine_id = machine; - h.boot_id = boot; - h.header_size = htole64(sizeof(h)); - h.start_usec = htole64(n * arg_interval); - h.interval_usec = htole64(arg_interval); - h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR); - h.fsprg_state_size = htole64(state_size); r = loop_write(fd, &h, sizeof(h), false); if (r < 0) { @@ -2092,10 +2088,13 @@ static int wait_for_change(sd_journal *j, int poll_fd) { return log_error_errno(errno, "Couldn't wait for journal event: %m"); } - if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */ + if (pollfds[1].revents & (POLLHUP|POLLERR|POLLNVAL)) /* STDOUT has been closed? */ return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Standard output has been closed."); + if (pollfds[0].revents & POLLNVAL) + return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "Change fd closed?"); + r = sd_journal_process(j); if (r < 0) return log_error_errno(r, "Failed to process journal events: %m"); @@ -2111,9 +2110,7 @@ int main(int argc, char *argv[]) { int n_shown = 0, r, poll_fd = -1; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be * split up into many files. */ diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index 5c31c4370..a5a78b774 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -2,6 +2,7 @@ #include "alloc-util.h" #include "audit-type.h" +#include "errno-util.h" #include "fd-util.h" #include "hexdecoct.h" #include "io-util.h" @@ -512,7 +513,7 @@ int server_open_audit(Server *s) { s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); if (s->audit_fd < 0) { - if (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT)) + if (ERRNO_IS_NOT_SUPPORTED(errno)) log_debug("Audit not supported in the kernel."); else log_warning_errno(errno, "Failed to create audit socket, ignoring: %m"); diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index ec404145e..6e7c806fd 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -315,7 +315,7 @@ static int server_read_dev_kmsg(Server *s) { if (IN_SET(errno, EAGAIN, EINTR, EPIPE)) return 0; - return log_error_errno(errno, "Failed to read from kernel: %m"); + return log_error_errno(errno, "Failed to read from /dev/kmsg: %m"); } dev_kmsg_record(s, buffer, l); @@ -423,7 +423,7 @@ int server_open_kernel_seqnum(Server *s) { assert(s); - /* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable, + /* We store the seqnum we last read in an mmapped file. That way we can just use it like a variable, * but it is persistent and automatically flushed at reboot. */ if (!s->read_kmsg) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 072b3d5ae..5865bf980 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1268,21 +1268,14 @@ int server_process_datagram( int *fds = NULL, v = 0; size_t n_fds = 0; - union { - struct cmsghdr cmsghdr; - - /* We use NAME_MAX space for the SELinux label - * here. The kernel currently enforces no - * limit, but according to suggestions from - * the SELinux people this will change and it - * will probably be identical to NAME_MAX. For - * now we use that, but this should be updated - * one day when the final limit is known. */ - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(struct timeval)) + - CMSG_SPACE(sizeof(int)) + /* fd */ - CMSG_SPACE(NAME_MAX)]; /* selinux label */ - } control = {}; + /* We use NAME_MAX space for the SELinux label here. The kernel currently enforces no limit, but + * according to suggestions from the SELinux people this will change and it will probably be + * identical to NAME_MAX. For now we use that, but this should be updated one day when the final + * limit is known. */ + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct timeval)) + + CMSG_SPACE(sizeof(int)) + /* fd */ + CMSG_SPACE(NAME_MAX) /* selinux label */) control; union sockaddr_union sa = {}; @@ -1640,23 +1633,24 @@ static int server_parse_config_file(Server *s) { /* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */ namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf"); - r = config_parse( - NULL, - namespaced, NULL, - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - CONFIG_PARSE_WARN, s); + r = config_parse(NULL, + namespaced, NULL, + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + CONFIG_PARSE_WARN, s, + NULL); if (r < 0) return r; return 0; } - return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf", - CONF_PATHS_NULSTR("systemd/journald.conf.d"), - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - CONFIG_PARSE_WARN, s); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/journald.conf", + CONF_PATHS_NULSTR("systemd/journald.conf.d"), + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + CONFIG_PARSE_WARN, s, NULL); } static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { @@ -1752,7 +1746,7 @@ static int server_open_hostname(Server *s) { r = sd_event_source_set_priority(s->hostname_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); if (r < 0) - return log_error_errno(r, "Failed to adjust priority of host name event source: %m"); + return log_error_errno(r, "Failed to adjust priority of hostname event source: %m"); return 0; } @@ -1956,7 +1950,7 @@ static int vl_method_synchronize(Varlink *link, JsonVariant *parameters, Varlink if (r < 0) return log_error_errno(r, "Failed to set event source destroy callback: %m"); - varlink_ref(link); /* The varlink object is now left to the destroy callack to unref */ + varlink_ref(link); /* The varlink object is now left to the destroy callback to unref */ r = sd_event_source_set_priority(event_source, SD_EVENT_PRIORITY_NORMAL+15); if (r < 0) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index ad9006217..241e2572e 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -58,6 +58,9 @@ typedef enum LineBreak { LINE_BREAK_NUL, LINE_BREAK_LINE_MAX, LINE_BREAK_EOF, + LINE_BREAK_PID_CHANGE, + _LINE_BREAK_MAX, + _LINE_BREAK_INVALID = -1, } LineBreak; struct StdoutStream { @@ -238,7 +241,11 @@ fail: return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); } -static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) { +static int stdout_stream_log( + StdoutStream *s, + const char *p, + LineBreak line_break) { + struct iovec *iovec; int priority; char syslog_priority[] = "PRIORITY=\0"; @@ -250,6 +257,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea assert(s); assert(p); + assert(line_break >= 0); + assert(line_break < _LINE_BREAK_MAX); + if (s->context) (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY); else if (pid_is_valid(s->ucred.pid)) { @@ -301,17 +311,20 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier); } - if (line_break != LINE_BREAK_NEWLINE) { - const char *c; + static const char * const line_break_field_table[_LINE_BREAK_MAX] = { + [LINE_BREAK_NEWLINE] = NULL, /* Do not add field if traditional newline */ + [LINE_BREAK_NUL] = "_LINE_BREAK=nul", + [LINE_BREAK_LINE_MAX] = "_LINE_BREAK=line-max", + [LINE_BREAK_EOF] = "_LINE_BREAK=eof", + [LINE_BREAK_PID_CHANGE] = "_LINE_BREAK=pid-change", + }; - /* If this log message was generated due to an uncommon line break then mention this in the log - * entry */ + const char *c = line_break_field_table[line_break]; - c = line_break == LINE_BREAK_NUL ? "_LINE_BREAK=nul" : - line_break == LINE_BREAK_LINE_MAX ? "_LINE_BREAK=line-max" : - "_LINE_BREAK=eof"; + /* If this log message was generated due to an uncommon line break then mention this in the log + * entry */ + if (c) iovec[n++] = IOVEC_MAKE_STRING(c); - } message = strjoin("MESSAGE=", p); if (message) @@ -322,8 +335,8 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea } static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) { - int r; char *orig; + int r; assert(s); assert(p); @@ -332,10 +345,9 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) { p = strstrip(p); /* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */ - if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING) { - log_warning("Control protocol line not properly terminated."); - return -EINVAL; - } + if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "Control protocol line not properly terminated."); switch (s->state) { @@ -425,21 +437,43 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) { assert_not_reached("Unknown stream state"); } -static int stdout_stream_scan(StdoutStream *s, bool force_flush) { - char *p; - size_t remaining; +static int stdout_stream_found( + StdoutStream *s, + char *p, + size_t l, + LineBreak line_break) { + + char saved; int r; assert(s); + assert(p); - p = s->buffer; - remaining = s->length; + /* Let's NUL terminate the specified buffer for this call, and revert back afterwards */ + saved = p[l]; + p[l] = 0; + r = stdout_stream_line(s, p, line_break); + p[l] = saved; - /* XXX: This function does nothing if (s->length == 0) */ + return r; +} + +static int stdout_stream_scan( + StdoutStream *s, + char *p, + size_t remaining, + LineBreak force_flush, + size_t *ret_consumed) { + + size_t consumed = 0; + int r; + + assert(s); + assert(p); for (;;) { LineBreak line_break; - size_t skip; + size_t skip, found; char *end1, *end2; end1 = memchr(p, '\n', remaining); @@ -447,62 +481,59 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { if (end2) { /* We found a NUL terminator */ - skip = end2 - p + 1; + found = end2 - p; + skip = found + 1; line_break = LINE_BREAK_NUL; } else if (end1) { /* We found a \n terminator */ - *end1 = 0; - skip = end1 - p + 1; + found = end1 - p; + skip = found + 1; line_break = LINE_BREAK_NEWLINE; } else if (remaining >= s->server->line_max) { /* Force a line break after the maximum line length */ - *(p + s->server->line_max) = 0; - skip = remaining; + found = skip = s->server->line_max; line_break = LINE_BREAK_LINE_MAX; } else break; - r = stdout_stream_line(s, p, line_break); + r = stdout_stream_found(s, p, found, line_break); if (r < 0) return r; - remaining -= skip; p += skip; + consumed += skip; + remaining -= skip; } - if (force_flush && remaining > 0) { - p[remaining] = 0; - r = stdout_stream_line(s, p, LINE_BREAK_EOF); + if (force_flush >= 0 && remaining > 0) { + r = stdout_stream_found(s, p, remaining, force_flush); if (r < 0) return r; - p += remaining; - remaining = 0; + consumed += remaining; } - if (p > s->buffer) { - memmove(s->buffer, p, remaining); - s->length = remaining; - } + if (ret_consumed) + *ret_consumed = consumed; return 0; } static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; StdoutStream *s = userdata; - struct ucred *ucred = NULL; - struct cmsghdr *cmsg; + size_t limit, consumed; + struct ucred *ucred; struct iovec iovec; - size_t limit; ssize_t l; + char *p; int r; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, - .msg_control = buf, - .msg_controllen = sizeof(buf), + .msg_control = &control, + .msg_controllen = sizeof(control), }; assert(s); @@ -523,7 +554,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also, * always leave room for a terminating NUL we might need to add. */ limit = MIN(s->allocated - 1, s->server->line_max); - + assert(s->length <= limit); iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); @@ -537,43 +568,42 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, cmsg_close_all(&msghdr); if (l == 0) { - stdout_stream_scan(s, true); + (void) stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_EOF, NULL); goto terminate; } - CMSG_FOREACH(cmsg, &msghdr) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { - assert(!ucred); - ucred = (struct ucred *)CMSG_DATA(cmsg); - break; - } - - /* Invalidate the context if the pid of the sender changed. - * This happens when a forked process inherits stdout / stderr - * from a parent. In this case getpeercred returns the ucred - * of the parent, which can be invalid if the parent has exited - * in the meantime. - */ + /* Invalidate the context if the PID of the sender changed. This happens when a forked process + * inherits stdout/stderr from a parent. In this case getpeercred() returns the ucred of the parent, + * which can be invalid if the parent has exited in the meantime. */ + ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); if (ucred && ucred->pid != s->ucred.pid) { - /* force out any previously half-written lines from a - * different process, before we switch to the new ucred - * structure for everything we just added */ - r = stdout_stream_scan(s, true); + /* Force out any previously half-written lines from a different process, before we switch to + * the new ucred structure for everything we just added */ + r = stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_PID_CHANGE, NULL); if (r < 0) goto terminate; - s->ucred = *ucred; - client_context_release(s->server, s->context); - s->context = NULL; + s->context = client_context_release(s->server, s->context); + + p = s->buffer + s->length; + } else { + p = s->buffer; + l += s->length; } - s->length += l; - r = stdout_stream_scan(s, false); + /* Always copy in the new credentials */ + if (ucred) + s->ucred = *ucred; + + r = stdout_stream_scan(s, p, l, _LINE_BREAK_INVALID, &consumed); if (r < 0) goto terminate; + /* Move what wasn't consumed to the front of the buffer */ + assert(consumed <= (size_t) l); + s->length = l - consumed; + memmove(s->buffer, p + consumed, s->length); + return 1; terminate: diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 1325b5638..46013c387 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -39,10 +39,7 @@ static void forward_syslog_iovec( .msg_iovlen = n_iovec, }; struct cmsghdr *cmsg; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; const char *j; int r; diff --git a/src/journal/lookup3.h b/src/journal/lookup3.h index 787921ffb..0a01269e4 100644 --- a/src/journal/lookup3.h +++ b/src/journal/lookup3.h @@ -13,7 +13,7 @@ void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_; -static inline uint64_t hash64(const void *data, size_t length) { +static inline uint64_t jenkins_hash64(const void *data, size_t length) { uint32_t a = 0, b = 0; jenkins_hashlittle2(data, length, &a, &b); diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index e2f7bd7ec..9eb3e1a62 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -162,7 +162,7 @@ static Window *window_add(MMapCache *m, MMapFileDescriptor *f, int prot, bool ke if (!m->last_unused || m->n_windows <= WINDOWS_MIN) { /* Allocate a new window */ - w = new0(Window, 1); + w = new(Window, 1); if (!w) return NULL; m->n_windows++; @@ -171,16 +171,17 @@ static Window *window_add(MMapCache *m, MMapFileDescriptor *f, int prot, bool ke /* Reuse an existing one */ w = m->last_unused; window_unlink(w); - zero(*w); } - w->cache = m; - w->fd = f; - w->prot = prot; - w->keep_always = keep_always; - w->offset = offset; - w->size = size; - w->ptr = ptr; + *w = (Window) { + .cache = m, + .fd = f, + .prot = prot, + .keep_always = keep_always, + .offset = offset, + .size = size, + .ptr = ptr, + }; LIST_PREPEND(by_fd, f->windows, w); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index e968b9de0..6fb0abb41 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -45,7 +45,9 @@ #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC) -#define REPLACE_VAR_MAX 256 +/* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as + * we want to be able to show all officially valid paths at least */ +#define REPLACE_VAR_MAX PATH_MAX #define DEFAULT_DATA_THRESHOLD (64*1024) @@ -115,28 +117,24 @@ static void detach_location(sd_journal *j) { journal_file_reset_location(f); } -static void reset_location(sd_journal *j) { - assert(j); - - detach_location(j); - zero(j->current_location); -} - static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) { assert(l); assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK)); assert(f); - assert(o->object.type == OBJECT_ENTRY); - l->type = type; - l->seqnum = le64toh(o->entry.seqnum); - l->seqnum_id = f->header->seqnum_id; - l->realtime = le64toh(o->entry.realtime); - l->monotonic = le64toh(o->entry.monotonic); - l->boot_id = o->entry.boot_id; - l->xor_hash = le64toh(o->entry.xor_hash); - - l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; + *l = (Location) { + .type = type, + .seqnum = le64toh(o->entry.seqnum), + .seqnum_id = f->header->seqnum_id, + .realtime = le64toh(o->entry.realtime), + .monotonic = le64toh(o->entry.monotonic), + .boot_id = o->entry.boot_id, + .xor_hash = le64toh(o->entry.xor_hash), + .seqnum_set = true, + .realtime_set = true, + .monotonic_set = true, + .xor_hash_set = true, + }; } static void set_location(sd_journal *j, JournalFile *f, Object *o) { @@ -242,7 +240,7 @@ static void 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; - le64_t le_hash; + uint64_t hash; assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); @@ -281,7 +279,9 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) assert(j->level1->type == MATCH_OR_TERM); assert(j->level2->type == MATCH_AND_TERM); - le_hash = htole64(hash64(data, size)); + /* Old-style Jenkins (unkeyed) hashing only here. We do not cover new-style siphash (keyed) hashing + * here, since it's different for each file, and thus can't be pre-calculated in the Match object. */ + hash = jenkins_hash64(data, size); LIST_FOREACH(matches, l3, j->level2->matches) { assert(l3->type == MATCH_OR_TERM); @@ -291,7 +291,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) /* Exactly the same match already? Then ignore * this addition */ - if (l4->le_hash == le_hash && + if (l4->hash == hash && l4->size == size && memcmp(l4->data, data, size) == 0) return 0; @@ -317,7 +317,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) if (!m) goto fail; - m->le_hash = le_hash; + m->hash = hash; m->size = size; m->data = memdup(data, size); if (!m->data) @@ -503,9 +503,16 @@ static int next_for_match( assert(f); if (m->type == MATCH_DISCRETE) { - uint64_t dp; + uint64_t dp, hash; - r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise + * we can use what we pre-calculated. */ + if (JOURNAL_HEADER_KEYED_HASH(f->header)) + hash = journal_file_hash_data(f, m->data, m->size); + else + hash = m->hash; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp); if (r <= 0) return r; @@ -592,9 +599,14 @@ static int find_location_for_match( assert(f); if (m->type == MATCH_DISCRETE) { - uint64_t dp; + uint64_t dp, hash; - r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + if (JOURNAL_HEADER_KEYED_HASH(f->header)) + hash = journal_file_hash_data(f, m->data, m->size); + else + hash = m->hash; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp); if (r <= 0) return r; @@ -1014,9 +1026,10 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { !realtime_set) return -EINVAL; - reset_location(j); - - j->current_location.type = LOCATION_SEEK; + detach_location(j); + j->current_location = (Location) { + .type = LOCATION_SEEK, + }; if (realtime_set) { j->current_location.realtime = (uint64_t) realtime; @@ -1129,11 +1142,14 @@ _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, u assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); - reset_location(j); - j->current_location.type = LOCATION_SEEK; - j->current_location.boot_id = boot_id; - j->current_location.monotonic = usec; - j->current_location.monotonic_set = true; + detach_location(j); + + j->current_location = (Location) { + .type = LOCATION_SEEK, + .boot_id = boot_id, + .monotonic = usec, + .monotonic_set = true, + }; return 0; } @@ -1142,10 +1158,13 @@ _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); - reset_location(j); - j->current_location.type = LOCATION_SEEK; - j->current_location.realtime = usec; - j->current_location.realtime_set = true; + detach_location(j); + + j->current_location = (Location) { + .type = LOCATION_SEEK, + .realtime = usec, + .realtime_set = true, + }; return 0; } @@ -1154,8 +1173,11 @@ _public_ int sd_journal_seek_head(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); - reset_location(j); - j->current_location.type = LOCATION_HEAD; + detach_location(j); + + j->current_location = (Location) { + .type = LOCATION_HEAD, + }; return 0; } @@ -1164,8 +1186,11 @@ _public_ int sd_journal_seek_tail(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); - reset_location(j); - j->current_location.type = LOCATION_TAIL; + detach_location(j); + + j->current_location = (Location) { + .type = LOCATION_TAIL, + }; return 0; } @@ -1719,7 +1744,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { goto fail; } } else { - int dfd; + _cleanup_close_ int dfd = -1; /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since * opendir() will take possession of the fd, and close it, which we don't want. */ @@ -1732,10 +1757,9 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { goto fail; } - d = fdopendir(dfd); + d = take_fdopendir(&dfd); if (!d) { r = -errno; - safe_close(dfd); goto fail; } @@ -2303,7 +2327,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** compression = o->object.flags & OBJECT_COMPRESSION_MASK; if (compression) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION r = decompress_startswith(compression, o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, @@ -2358,7 +2382,10 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da uint64_t l; int compression; - l = le64toh(o->object.size) - offsetof(Object, data.payload); + l = le64toh(READ_NOW(o->object.size)); + if (l < offsetof(Object, data.payload)) + return -EBADMSG; + l -= offsetof(Object, data.payload); t = (size_t) l; /* We can't read objects larger than 4G on a 32bit machine */ @@ -2367,7 +2394,7 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da compression = o->object.flags & OBJECT_COMPRESSION_MASK; if (compression) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION size_t rsize; int r; @@ -2435,6 +2462,19 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t return 1; } +_public_ int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *size) { + for (;;) { + int r; + + r = sd_journal_enumerate_data(j, data, size); + if (r >= 0) + return r; + if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r)) + return r; + j->current_field++; /* Try with the next field */ + } +} + _public_ void sd_journal_restart_data(sd_journal *j) { if (!j) return; @@ -2975,6 +3015,20 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ } } +_public_ int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *size) { + for (;;) { + int r; + + r = sd_journal_enumerate_unique(j, data, size); + if (r >= 0) + return r; + if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r)) + return r; + /* Try with the next field. sd_journal_enumerate_unique() modifies state, so on the next try + * we will access the next field. */ + } +} + _public_ void sd_journal_restart_unique(sd_journal *j) { if (!j) return; diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index ba14d922e..158847f3e 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -116,9 +116,8 @@ static void test_catalog_import_merge(void) { h = test_import(input, -1, 0); assert_se(ordered_hashmap_size(h) == 1); - ORDERED_HASHMAP_FOREACH(payload, h, j) { + ORDERED_HASHMAP_FOREACH(payload, h, j) assert_se(streq(combined, payload)); - } } static void test_catalog_import_merge_no_body(void) { @@ -149,9 +148,8 @@ static void test_catalog_import_merge_no_body(void) { h = test_import(input, -1, 0); assert_se(ordered_hashmap_size(h) == 1); - ORDERED_HASHMAP_FOREACH(payload, h, j) { + ORDERED_HASHMAP_FOREACH(payload, h, j) assert_se(streq(combined, payload)); - } } static void test_catalog_update(const char *database) { diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 100599705..35823a8da 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -17,7 +17,7 @@ typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, typedef int (decompress_t)(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION static usec_t arg_duration; static size_t arg_start; @@ -143,7 +143,7 @@ static void test_compress_decompress(const char* label, const char* type, #endif int main(int argc, char *argv[]) { -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION test_setup_logging(LOG_INFO); if (argc >= 2) { @@ -167,6 +167,9 @@ int main(int argc, char *argv[]) { #endif #if HAVE_LZ4 test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4); +#endif +#if HAVE_ZSTD + test_compress_decompress("ZSTD", i, compress_blob_zstd, decompress_blob_zstd); #endif } return 0; diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index fac2b43c4..f50fb0ace 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -29,6 +29,8 @@ # define LZ4_OK -EPROTONOSUPPORT #endif +#define HUGE_SIZE (4096*1024) + typedef int (compress_blob_t)(const void *src, uint64_t src_size, void *dst, size_t dst_alloc_size, size_t *dst_size); typedef int (decompress_blob_t)(const void *src, uint64_t src_size, @@ -42,20 +44,20 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size, typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes); typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); -#if HAVE_XZ || HAVE_LZ4 -static void test_compress_decompress(int compression, - compress_blob_t compress, - decompress_blob_t decompress, - const char *data, - size_t data_len, - bool may_fail) { +#if HAVE_COMPRESSION +_unused_ static void test_compress_decompress(const char *compression, + compress_blob_t compress, + decompress_blob_t decompress, + const char *data, + size_t data_len, + bool may_fail) { char compressed[512]; size_t csize, usize = 0; _cleanup_free_ char *decompressed = NULL; int r; log_info("/* testing %s %s blob compression/decompression */", - object_compressed_to_string(compression), data); + compression, data); r = compress(data, data_len, compressed, sizeof(compressed), &csize); if (r == -ENOBUFS) { @@ -86,12 +88,12 @@ static void test_compress_decompress(int compression, memzero(decompressed, usize); } -static void test_decompress_startswith(int compression, - compress_blob_t compress, - decompress_sw_t decompress_sw, - const char *data, - size_t data_len, - bool may_fail) { +_unused_ static void test_decompress_startswith(const char *compression, + compress_blob_t compress, + decompress_sw_t decompress_sw, + const char *data, + size_t data_len, + bool may_fail) { char *compressed; _cleanup_free_ char *compressed1 = NULL, *compressed2 = NULL, *decompressed = NULL; @@ -99,7 +101,7 @@ static void test_decompress_startswith(int compression, int r; log_info("/* testing decompress_startswith with %s on %.20s text */", - object_compressed_to_string(compression), data); + compression, data); #define BUFSIZE_1 512 #define BUFSIZE_2 20000 @@ -134,9 +136,9 @@ static void test_decompress_startswith(int compression, assert_se(r > 0); } -static void test_decompress_startswith_short(int compression, - compress_blob_t compress, - decompress_sw_t decompress_sw) { +_unused_ static void test_decompress_startswith_short(const char *compression, + compress_blob_t compress, + decompress_sw_t decompress_sw) { #define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @@ -144,7 +146,7 @@ static void test_decompress_startswith_short(int compression, size_t i, csize; int r; - log_info("/* %s with %s */", __func__, object_compressed_to_string(compression)); + log_info("/* %s with %s */", __func__, compression); r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize); assert_se(r == 0); @@ -160,11 +162,11 @@ static void test_decompress_startswith_short(int compression, } } -static void test_compress_stream(int compression, - const char* cat, - compress_stream_t compress, - decompress_stream_t decompress, - const char *srcfile) { +_unused_ static void test_compress_stream(const char *compression, + const char *cat, + compress_stream_t compress, + decompress_stream_t decompress, + const char *srcfile) { _cleanup_close_ int src = -1, dst = -1, dst2 = -1; _cleanup_(unlink_tempfilep) char @@ -180,8 +182,7 @@ static void test_compress_stream(int compression, return; } - log_debug("/* testing %s compression */", - object_compressed_to_string(compression)); + log_debug("/* testing %s compression */", compression); log_debug("/* create source from %s */", srcfile); @@ -231,10 +232,12 @@ static void test_lz4_decompress_partial(void) { int r; _cleanup_free_ char *huge = NULL; -#define HUGE_SIZE (4096*1024) + log_debug("/* %s */", __func__); + assert_se(huge = malloc(HUGE_SIZE)); - memset(huge, 'x', HUGE_SIZE); - memcpy(huge, "HUGE=", 5); + memcpy(huge, "HUGE=", STRLEN("HUGE=")); + memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1); + huge[HUGE_SIZE - 1] = '\0'; r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size); assert_se(r >= 0); @@ -264,8 +267,8 @@ static void test_lz4_decompress_partial(void) { #endif int main(int argc, char *argv[]) { -#if HAVE_XZ || HAVE_LZ4 - const char text[] = +#if HAVE_COMPRESSION + _unused_ const char text[] = "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF" "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"; @@ -274,70 +277,96 @@ int main(int argc, char *argv[]) { char data[512] = "random\0"; - char huge[4096*1024]; - memset(huge, 'x', sizeof(huge)); - memcpy(huge, "HUGE=", 5); - char_array_0(huge); + _cleanup_free_ char *huge = NULL; + + assert_se(huge = malloc(HUGE_SIZE)); + memcpy(huge, "HUGE=", STRLEN("HUGE=")); + memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1); + huge[HUGE_SIZE - 1] = '\0'; test_setup_logging(LOG_DEBUG); random_bytes(data + 7, sizeof(data) - 7); #if HAVE_XZ - test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz, + test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz, text, sizeof(text), false); - test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz, + test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz, data, sizeof(data), true); - test_decompress_startswith(OBJECT_COMPRESSED_XZ, + test_decompress_startswith("XZ", compress_blob_xz, decompress_startswith_xz, text, sizeof(text), false); - test_decompress_startswith(OBJECT_COMPRESSED_XZ, + test_decompress_startswith("XZ", compress_blob_xz, decompress_startswith_xz, data, sizeof(data), true); - test_decompress_startswith(OBJECT_COMPRESSED_XZ, + test_decompress_startswith("XZ", compress_blob_xz, decompress_startswith_xz, - huge, sizeof(huge), true); + huge, HUGE_SIZE, true); - test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat", + test_compress_stream("XZ", "xzcat", compress_stream_xz, decompress_stream_xz, srcfile); - test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz); + test_decompress_startswith_short("XZ", compress_blob_xz, decompress_startswith_xz); #else log_info("/* XZ test skipped */"); #endif #if HAVE_LZ4 - test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4, + test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4, text, sizeof(text), false); - test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4, + test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4, data, sizeof(data), true); - test_decompress_startswith(OBJECT_COMPRESSED_LZ4, + test_decompress_startswith("LZ4", compress_blob_lz4, decompress_startswith_lz4, text, sizeof(text), false); - test_decompress_startswith(OBJECT_COMPRESSED_LZ4, + test_decompress_startswith("LZ4", compress_blob_lz4, decompress_startswith_lz4, data, sizeof(data), true); - test_decompress_startswith(OBJECT_COMPRESSED_LZ4, + test_decompress_startswith("LZ4", compress_blob_lz4, decompress_startswith_lz4, - huge, sizeof(huge), true); + huge, HUGE_SIZE, true); - test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat", + test_compress_stream("LZ4", "lz4cat", compress_stream_lz4, decompress_stream_lz4, srcfile); test_lz4_decompress_partial(); - test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4); + test_decompress_startswith_short("LZ4", compress_blob_lz4, decompress_startswith_lz4); #else log_info("/* LZ4 test skipped */"); #endif +#if HAVE_ZSTD + test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd, + text, sizeof(text), false); + test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd, + data, sizeof(data), true); + + test_decompress_startswith("ZSTD", + compress_blob_zstd, decompress_startswith_zstd, + text, sizeof(text), false); + test_decompress_startswith("ZSTD", + compress_blob_zstd, decompress_startswith_zstd, + data, sizeof(data), true); + test_decompress_startswith("ZSTD", + compress_blob_zstd, decompress_startswith_zstd, + huge, HUGE_SIZE, true); + + test_compress_stream("ZSTD", "zstdcat", + compress_stream_zstd, decompress_stream_zstd, srcfile); + + test_decompress_startswith_short("ZSTD", compress_blob_zstd, decompress_startswith_zstd); +#else + log_info("/* ZSTD test skipped */"); +#endif + return 0; #else - log_info("/* XZ and LZ4 tests skipped */"); + log_info("/* XZ, LZ4 and ZSTD tests skipped */"); return EXIT_TEST_SKIP; #endif } diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c index 484308e56..4265735f0 100644 --- a/src/journal/test-journal-send.c +++ b/src/journal/test-journal-send.c @@ -5,11 +5,23 @@ #include #include "sd-journal.h" - +#include "fileio.h" #include "macro.h" +#include "memory-util.h" -int main(int argc, char *argv[]) { - char huge[4096*1024]; +static void test_journal_print(void) { + 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); + assert_se(sd_journal_print(LOG_INFO, "X%*sY", LONG_LINE_MAX - 8 - 3, "ZZZ") == 0); + assert_se(sd_journal_print(LOG_INFO, "X%*sY", LONG_LINE_MAX - 8 - 2, "ZZZ") == -ENOBUFS); +} + +static void test_journal_send(void) { + _cleanup_free_ char *huge = NULL; + +#define HUGE_SIZE (4096*1024) + assert_se(huge = malloc(HUGE_SIZE)); /* utf-8 and non-utf-8, message-less and message-ful iovecs */ struct iovec graph1[] = { @@ -36,9 +48,9 @@ int main(int argc, char *argv[]) { assert_se(sd_journal_perror("") == 0); - memset(huge, 'x', sizeof(huge)); - memcpy(huge, "HUGE=", 5); - char_array_0(huge); + memcpy(huge, "HUGE=", STRLEN("HUGE=")); + memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1); + huge[HUGE_SIZE - 1] = '\0'; assert_se(sd_journal_send("MESSAGE=Huge field attached", huge, @@ -78,7 +90,13 @@ int main(int argc, char *argv[]) { assert_se(sd_journal_sendv(graph2, 1) == 0); assert_se(sd_journal_sendv(message1, 1) == 0); assert_se(sd_journal_sendv(message2, 1) == 0); +} +int main(int argc, char *argv[]) { + test_journal_print(); + test_journal_send(); + + /* Sleep a bit to make it easy for journald to collect metadata. */ sleep(1); return 0; diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 6d97bc5ce..50aab11c6 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -58,7 +58,7 @@ static void verify_contents(sd_journal *j, unsigned skip) { assert_se(i == N_ENTRIES); } -int main(int argc, char *argv[]) { +static void run_test(void) { JournalFile *one, *two, *three; char t[] = "/var/tmp/journal-stream-XXXXXX"; unsigned i; @@ -68,12 +68,6 @@ int main(int argc, char *argv[]) { size_t l; dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL; - /* 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_setup_logging(LOG_DEBUG); - assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL); @@ -177,6 +171,22 @@ int main(int argc, char *argv[]) { printf("%.*s\n", (int) l, (const char*) data); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); +} + +int main(int argc, char *argv[]) { + + /* 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_setup_logging(LOG_DEBUG); + + /* Run this test twice. Once with old hashing and once with new hashing */ + assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "1", 1) >= 0); + run_test(); + + assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "0", 1) >= 0); + run_test(); return 0; } diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 7f56668af..5850bb8ea 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -157,7 +157,7 @@ static void test_empty(void) { (void) journal_file_close(f4); } -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { dual_timestamp ts; JournalFile *f; @@ -251,7 +251,7 @@ int main(int argc, char *argv[]) { test_non_empty(); test_empty(); -#if HAVE_XZ || HAVE_LZ4 +#if HAVE_COMPRESSION test_min_compress_size(); #endif diff --git a/src/kernel-install/meson.build b/src/kernel-install/meson.build index 261c3aaae..9ae342dfb 100644 --- a/src/kernel-install/meson.build +++ b/src/kernel-install/meson.build @@ -1,14 +1,18 @@ # SPDX-License-Identifier: LGPL-2.1+ -install_data('kernel-install', - install_mode : 'rwxr-xr-x', - install_dir : bindir) +want_kernel_install = get_option('kernel-install') -install_data('00-entry-directory.install', - '50-depmod.install', - '90-loaderentry.install', - install_mode : 'rwxr-xr-x', - install_dir : kernelinstalldir) +if want_kernel_install + install_data('kernel-install', + install_mode : 'rwxr-xr-x', + install_dir : bindir) -meson.add_install_script('sh', '-c', - mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d'))) + install_data('00-entry-directory.install', + '50-depmod.install', + '90-loaderentry.install', + install_mode : 'rwxr-xr-x', + install_dir : kernelinstalldir) + + meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d'))) +endif diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index c01c1cf6d..d0610a32e 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -15,7 +15,6 @@ #include "udev-util.h" #include "virt.h" -#define SYSTEMD_PEN 43793 #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 */ diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index b3115125d..76abd6583 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -8,6 +8,8 @@ #include "time-util.h" #include "unaligned.h" +#define SYSTEMD_PEN 43793 + typedef enum DUIDType { DUID_TYPE_LLT = 1, DUID_TYPE_EN = 2, diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 6a803d7b0..7e8149487 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -7,7 +7,6 @@ #include #include -#include #include #include "sd-dhcp-client.h" @@ -23,6 +22,11 @@ typedef struct sd_dhcp_option { size_t length; } sd_dhcp_option; +typedef struct DHCPServerData { + struct in_addr *addr; + size_t size; +} DHCPServerData; + extern const struct hash_ops dhcp_option_hash_ops; int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index a2d0f8bd5..66222eadd 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -5,11 +5,9 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include -#include - #include "sd-dhcp-client.h" +#include "dhcp-internal.h" #include "dhcp-protocol.h" #include "list.h" #include "util.h" @@ -52,14 +50,7 @@ struct sd_dhcp_lease { struct in_addr *router; size_t router_size; - struct in_addr *dns; - size_t dns_size; - - struct in_addr *ntp; - size_t ntp_size; - - struct in_addr *sip; - size_t sip_size; + DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; struct sd_dhcp_route *static_route; size_t static_route_size, static_route_allocated; @@ -91,6 +82,3 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len); - -int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); -int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 42c3ceb8b..64c18ed57 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -19,6 +19,7 @@ typedef enum DHCPRawOption { DHCP_RAW_OPTION_DATA_UINT32, DHCP_RAW_OPTION_DATA_STRING, DHCP_RAW_OPTION_DATA_IPV4ADDRESS, + DHCP_RAW_OPTION_DATA_IPV6ADDRESS, _DHCP_RAW_OPTION_DATA_MAX, _DHCP_RAW_OPTION_DATA_INVALID, } DHCPRawOption; @@ -55,10 +56,10 @@ struct sd_dhcp_server { char *timezone; - struct in_addr *ntp, *dns, *sip; - unsigned n_ntp, n_dns, n_sip; + DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; - OrderedHashmap *raw_option; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; bool emit_router; @@ -67,6 +68,9 @@ struct sd_dhcp_server { DHCPLease invalid_lease; uint32_t max_lease_time, default_lease_time; + + sd_dhcp_server_callback_t callback; + void *callback_userdata; }; typedef struct DHCPRequest { diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 517e357d3..baf7bb2ef 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -11,9 +11,21 @@ #include "sd-event.h" #include "list.h" +#include "hashmap.h" #include "macro.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; @@ -87,17 +99,20 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, 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 **user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_status(DHCP6Option *option, size_t len); -int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr); -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); +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); diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index f82afe6a0..e2efa8bbe 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -17,16 +17,16 @@ #include "fd-util.h" #include "socket-util.h" -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { +int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { union sockaddr_union src = { .in6.sin6_family = AF_INET6, .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), - .in6.sin6_scope_id = index, + .in6.sin6_scope_id = ifindex, }; _cleanup_close_ int s = -1; int r; - assert(index > 0); + assert(ifindex > 0); assert(local_address); src.in6.sin6_addr = *local_address; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 9f5352a60..fa4358768 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -9,6 +9,7 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" +#include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" @@ -78,6 +79,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { + sd_dhcp6_option *options; + Iterator i; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(vendor_options); + + ORDERED_HASHMAP_FOREACH(options, vendor_options, i) { + _cleanup_free_ uint8_t *p = NULL; + size_t total; + + total = 4 + 2 + 2 + options->length; + + p = malloc(total); + if (!p) + return -ENOMEM; + + unaligned_write_be32(p, options->enterprise_identifier); + unaligned_write_be16(p + 4, options->option); + unaligned_write_be16(p + 6, options->length); + memcpy(p + 8, options->data, options->length); + + r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p); + if (r < 0) + return r; + } + + return 0; +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; @@ -167,6 +201,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) { + _cleanup_free_ uint8_t *p = NULL; + size_t total = 0, offset = 0; + char **s; + + assert_return(buf && *buf && buflen && user_class, -EINVAL); + + STRV_FOREACH(s, user_class) { + size_t len = strlen(*s); + uint8_t *q; + + if (len > 0xffff) + return -ENAMETOOLONG; + q = realloc(p, total + len + 2); + if (!q) + return -ENOMEM; + + p = q; + + unaligned_write_be16(&p[offset], len); + memcpy(&p[offset + 2], *s, len); + + offset += 2 + len; + total += 2 + len; + } + + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); +} + +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) { + _cleanup_free_ uint8_t *p = NULL; + uint32_t enterprise_identifier; + size_t total, offset; + char **s; + + assert(buf); + assert(*buf); + assert(buflen); + assert(vendor_class); + + enterprise_identifier = htobe32(SYSTEMD_PEN); + + p = memdup(&enterprise_identifier, sizeof(enterprise_identifier)); + if (!p) + return -ENOMEM; + + total = sizeof(enterprise_identifier); + offset = total; + + STRV_FOREACH(s, vendor_class) { + size_t len = strlen(*s); + uint8_t *q; + + q = realloc(p, total + len + 2); + if (!q) + return -ENOMEM; + + p = q; + + unaligned_write_be16(&p[offset], len); + memcpy(&p[offset + 2], *s, len); + + offset += 2 + len; + total += 2 + len; + } + + return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); +} + int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); @@ -347,13 +450,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } -int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { - uint16_t iatype, optlen; - size_t i, len; - int r = 0, status; - uint16_t opt; - size_t iaaddr_offset; +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) { uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; + uint16_t iatype, optlen; + size_t iaaddr_offset; + int r = 0, status; + size_t i, len; + uint16_t opt; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); @@ -463,11 +566,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); if (status < 0) return status; - if (status > 0) { - log_dhcp6_client(client, "IA status %d", - status); - return -EINVAL; + if (status > 0) { + if (ret_status_code) + *ret_status_code = status; + + log_dhcp6_client(client, "IA status %s", + dhcp6_message_status_to_string(status)); + + return 0; } break; @@ -511,7 +618,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } - return 0; + if (ret_status_code) + *ret_status_code = 0; + + return 1; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, @@ -597,3 +707,44 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * return idx; } + +static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { + if (!i) + return NULL; + + free(i->data); + return mfree(i); +} + +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) { + assert_return(ret, -EINVAL); + assert_return(length == 0 || data, -EINVAL); + + _cleanup_free_ void *q = memdup(data, length); + if (!q) + return -ENOMEM; + + sd_dhcp6_option *p = new(sd_dhcp6_option, 1); + if (!p) + return -ENOMEM; + + *p = (sd_dhcp6_option) { + .n_ref = 1, + .option = option, + .enterprise_identifier = enterprise_identifier, + .length = length, + .data = TAKE_PTR(q), + }; + + *ret = p; + return 0; +} + +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free); +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + dhcp6_option_hash_ops, + void, + trivial_hash_func, + trivial_compare_func, + sd_dhcp6_option, + sd_dhcp6_option_unref); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index ffae4453a..f7a270286 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -82,14 +82,35 @@ enum { DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, }; +/* + * RFC 8415, RFC 5007 and RFC 7653 status codes: + * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5 + */ enum { - DHCP6_STATUS_SUCCESS = 0, - DHCP6_STATUS_UNSPEC_FAIL = 1, - DHCP6_STATUS_NO_ADDRS_AVAIL = 2, - DHCP6_STATUS_NO_BINDING = 3, - DHCP6_STATUS_NOT_ON_LINK = 4, - DHCP6_STATUS_USE_MULTICAST = 5, - _DHCP6_STATUS_MAX = 6, + DHCP6_STATUS_SUCCESS = 0, + DHCP6_STATUS_UNSPEC_FAIL = 1, + DHCP6_STATUS_NO_ADDRS_AVAIL = 2, + DHCP6_STATUS_NO_BINDING = 3, + DHCP6_STATUS_NOT_ON_LINK = 4, + DHCP6_STATUS_USE_MULTICAST = 5, + DHCP6_STATUS_NO_PREFIX_AVAIL = 6, + DHCP6_STATUS_UNKNOWN_QUERY_TYPE = 7, + DHCP6_STATUS_MALFORMED_QUERY = 8, + DHCP6_STATUS_NOT_CONFIGURED = 9, + DHCP6_STATUS_NOT_ALLOWED = 10, + DHCP6_STATUS_QUERY_TERMINATED = 11, + DHCP6_STATUS_DATA_MISSING = 12, + DHCP6_STATUS_CATCHUP_COMPLETE = 13, + DHCP6_STATUS_NOT_SUPPORTED = 14, + DHCP6_STATUS_TLS_CONNECTION_REFUSED = 15, + DHCP6_STATUS_ADDRESS_IN_USE = 16, + DHCP6_STATUS_CONFIGURATION_CONFLICT = 17, + DHCP6_STATUS_MISSING_BINDING_INFORMATION = 18, + DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19, + DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20, + DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, + DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, + _DHCP6_STATUS_MAX = 23, }; enum { diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index dbb1e51a0..d9690293f 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -81,11 +81,11 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter, return TAKE_FD(s); } -int icmp6_bind_router_solicitation(int index) { +int icmp6_bind_router_solicitation(int ifindex) { struct icmp6_filter filter = {}; struct ipv6_mreq mreq = { .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT, - .ipv6mr_interface = index, + .ipv6mr_interface = ifindex, }; ICMP6_FILTER_SETBLOCKALL(&filter); @@ -94,11 +94,11 @@ int icmp6_bind_router_solicitation(int index) { return icmp6_bind_router_message(&filter, &mreq); } -int icmp6_bind_router_advertisement(int index) { +int icmp6_bind_router_advertisement(int ifindex) { struct icmp6_filter filter = {}; struct ipv6_mreq mreq = { .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT, - .ipv6mr_interface = index, + .ipv6mr_interface = ifindex, }; ICMP6_FILTER_SETBLOCKALL(&filter); @@ -147,11 +147,9 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst, triple_timestamp *timestamp) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */ - CMSG_SPACE(sizeof(struct timeval))]; - } control = {}; + + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */ + CMSG_SPACE(sizeof(struct timeval))) control; struct iovec iov = {}; union sockaddr_union sa = {}; struct msghdr msg = { @@ -167,9 +165,9 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst, iov = IOVEC_MAKE(buffer, size); - len = recvmsg(fd, &msg, MSG_DONTWAIT); + len = recvmsg_safe(fd, &msg, MSG_DONTWAIT); if (len < 0) - return -errno; + return (int) len; if ((size_t) len != size) return -EINVAL; diff --git a/src/libsystemd-network/icmp6-util.h b/src/libsystemd-network/icmp6-util.h index 725a68086..ac68ded1f 100644 --- a/src/libsystemd-network/icmp6-util.h +++ b/src/libsystemd-network/icmp6-util.h @@ -17,8 +17,8 @@ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } -int icmp6_bind_router_solicitation(int index); -int icmp6_bind_router_advertisement(int index); +int icmp6_bind_router_solicitation(int ifindex); +int icmp6_bind_router_advertisement(int ifindex); int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr); int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst, triple_timestamp *timestamp); diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index 1e9fe7303..02645b2bc 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -50,6 +50,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) { free(n->port_description); free(n->system_name); free(n->system_description); + free(n->mud_url); free(n->chassis_id_as_string); free(n->port_id_as_string); free(n); @@ -292,9 +293,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { break; - case SD_LLDP_TYPE_PRIVATE: + case SD_LLDP_TYPE_PRIVATE: { if (length < 4) log_lldp("Found private TLV that is too short, ignoring."); + else { + /* RFC 8520: MUD URL */ + if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 && + p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) { + r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1, + length - 1 - sizeof(SD_LLDP_OUI_MUD)); + if (r < 0) + return r; + } + } + } break; } @@ -593,6 +605,17 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch return 0; } +_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->mud_url) + return -ENODATA; + + *ret = n->mud_url; + return 0; +} + _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { assert_return(n, -EINVAL); assert_return(ret, -EINVAL); diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h index 62dbff42c..74175edf5 100644 --- a/src/libsystemd-network/lldp-neighbor.h +++ b/src/libsystemd-network/lldp-neighbor.h @@ -54,6 +54,7 @@ struct sd_lldp_neighbor { char *port_description; char *system_name; char *system_description; + char *mud_url; uint16_t port_vlan_id; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 0f4ba7a35..459d13ad7 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -660,27 +660,27 @@ int config_parse_bridge_port_priority( size_t serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size, - bool with_leading_space, + bool *with_leading_space, bool (*predicate)(const struct in_addr *addr)) { - size_t count; - size_t i; - assert(f); assert(addresses); - count = 0; + size_t count = 0; + bool _space = false; + if (!with_leading_space) + with_leading_space = &_space; - for (i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { char sbuf[INET_ADDRSTRLEN]; if (predicate && !predicate(&addresses[i])) continue; - if (with_leading_space) + + if (*with_leading_space) fputc(' ', f); - else - with_leading_space = true; fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f); count++; + *with_leading_space = true; } return count; @@ -722,20 +722,22 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { return size; } -void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) { - unsigned i; - +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) { assert(f); assert(addresses); assert(size); - for (i = 0; i < size; i++) { + bool _space = false; + if (!with_leading_space) + with_leading_space = &_space; + + for (size_t i = 0; i < size; i++) { char buffer[INET6_ADDRSTRLEN]; - fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); - - if (i < size - 1) + if (*with_leading_space) fputc(' ', f); + fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); + *with_leading_space = true; } } @@ -776,8 +778,6 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { } void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { - unsigned i; - assert(f); assert(key); assert(routes); @@ -785,7 +785,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz fprintf(f, "%s=", key); - for (i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { char sbuf[INET_ADDRSTRLEN]; struct in_addr dest, gw; uint8_t length; @@ -794,8 +794,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); - fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length); - fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": ""); + fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length); + fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": ""); } fputs("\n", f); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 40b008ebc..e4c11235b 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -8,7 +8,6 @@ #include "sd-dhcp-lease.h" #include "conf-parser.h" -#include "def.h" #include "set.h" #include "strv.h" @@ -52,15 +51,17 @@ const char *net_get_name_persistent(sd_device *device); size_t serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size, - bool with_leading_space, + bool *with_leading_space, bool (*predicate)(const struct in_addr *addr)); int deserialize_in_addrs(struct in_addr **addresses, const char *string); void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, - size_t size); + size_t size, + bool *with_leading_space); int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */ struct sd_dhcp_route; +struct sd_dhcp_lease; void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); @@ -68,4 +69,5 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); -#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) +int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); +int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index b73591539..a83ffc342 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -27,6 +27,8 @@ #include "random-util.h" #include "string-util.h" #include "strv.h" +#include "utf8.h" +#include "web-util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -34,6 +36,32 @@ #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) +typedef struct sd_dhcp_client_id { + uint8_t type; + union { + struct { + /* 0: Generic (non-LL) (RFC 2132) */ + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ gen; + struct { + /* 1: Ethernet Link-Layer (RFC 2132) */ + uint8_t haddr[ETH_ALEN]; + } _packed_ eth; + struct { + /* 2 - 254: ARP/Link-Layer (RFC 2132) */ + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* 255: Node-specific (RFC 4361) */ + be32_t iaid; + struct duid duid; + } _packed_ ns; + struct { + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ raw; + }; +} _packed_ sd_dhcp_client_id; + struct sd_dhcp_client { unsigned n_ref; @@ -55,41 +83,20 @@ struct sd_dhcp_client { uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; - struct { - uint8_t type; - union { - struct { - /* 0: Generic (non-LL) (RFC 2132) */ - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ gen; - struct { - /* 1: Ethernet Link-Layer (RFC 2132) */ - uint8_t haddr[ETH_ALEN]; - } _packed_ eth; - struct { - /* 2 - 254: ARP/Link-Layer (RFC 2132) */ - uint8_t haddr[0]; - } _packed_ ll; - struct { - /* 255: Node-specific (RFC 4361) */ - be32_t iaid; - struct duid duid; - } _packed_ ns; - struct { - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ raw; - }; - } _packed_ client_id; + sd_dhcp_client_id client_id; size_t client_id_len; char *hostname; char *vendor_class_identifier; + char *mudurl; char **user_class; uint32_t mtu; + uint32_t fallback_lease_lifetime; uint32_t xid; usec_t start_time; uint64_t attempt; uint64_t max_attempts; - OrderedHashmap *options; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -147,6 +154,60 @@ static int client_receive_message_udp( void *userdata); static void client_stop(sd_dhcp_client *client, int error); +int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) { + const sd_dhcp_client_id *client_id = data; + _cleanup_free_ char *t = NULL; + int r = 0; + + assert_return(data, -EINVAL); + assert_return(len >= 1, -EINVAL); + assert_return(ret, -EINVAL); + + len -= 1; + if (len > MAX_CLIENT_ID_LEN) + return -EINVAL; + + switch (client_id->type) { + case 0: + if (utf8_is_printable((char *) client_id->gen.data, len)) + r = asprintf(&t, "%.*s", (int) len, client_id->gen.data); + else + r = asprintf(&t, "DATA"); + break; + case 1: + if (len != sizeof_field(sd_dhcp_client_id, eth)) + return -EINVAL; + + r = asprintf(&t, "%x:%x:%x:%x:%x:%x", + client_id->eth.haddr[0], + client_id->eth.haddr[1], + client_id->eth.haddr[2], + client_id->eth.haddr[3], + client_id->eth.haddr[4], + client_id->eth.haddr[5]); + break; + case 2 ... 254: + r = asprintf(&t, "ARP/LL"); + break; + case 255: + if (len < 6) + return -EINVAL; + + uint32_t iaid = be32toh(client_id->ns.iaid); + uint16_t duid_type = be16toh(client_id->ns.duid.type); + if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0) + return -EINVAL; + + r = asprintf(&t, "IAID:0x%x/DUID", iaid); + break; + } + + if (r < 0) + return -ENOMEM; + *ret = TAKE_PTR(t); + return 0; +} + int sd_dhcp_client_set_callback( sd_dhcp_client *client, sd_dhcp_client_callback_t cb, @@ -315,7 +376,7 @@ int sd_dhcp_client_set_client_id( /* For hardware types, log debug message about unexpected data length. * * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only - * last last 8 bytes of the address are stable and suitable to put into + * the last 8 bytes of the address are stable and suitable to put into * the client-id. The caller is advised to account for that. */ if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) || (type == ARPHRD_INFINIBAND && data_len != 8)) @@ -492,6 +553,18 @@ int sd_dhcp_client_set_vendor_class_identifier( return free_and_strdup(&client->vendor_class_identifier, vci); } +int sd_dhcp_client_set_mud_url( + sd_dhcp_client *client, + const char *mudurl) { + + assert_return(client, -EINVAL); + assert_return(mudurl, -EINVAL); + assert_return(strlen(mudurl) <= 255, -EINVAL); + assert_return(http_url_is_valid(mudurl), -EINVAL); + + return free_and_strdup(&client->mudurl, mudurl); +} + int sd_dhcp_client_set_user_class( sd_dhcp_client *client, const char* const* user_class) { @@ -540,17 +613,17 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt return 0; } -int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) { +int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) { int r; assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops); + r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops); if (r < 0) return r; - r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v); + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); if (r < 0) return r; @@ -558,6 +631,25 @@ int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) { return 0; } +int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) { + int r; + + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops); + if (r < 0) + return -ENOMEM; + + r = ordered_hashmap_put(client->vendor_options, v, v); + if (r < 0) + return r; + + sd_dhcp_option_ref(v); + + return 1; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); @@ -578,6 +670,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) { return 0; } +int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) { + assert_return(client, -EINVAL); + assert_return(fallback_lease_lifetime > 0, -EINVAL); + + client->fallback_lease_lifetime = fallback_lease_lifetime; + + return 0; +} + static int client_notify(sd_dhcp_client *client, int event) { assert(client); @@ -816,11 +917,82 @@ static int dhcp_client_send_raw( packet, len); } +static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) { + sd_dhcp_option *j; + Iterator i; + int r; + + assert(client); + + if (client->hostname) { + /* According to RFC 4702 "clients that send the Client FQDN option in + their messages MUST NOT also send the Host Name option". Just send + one of the two depending on the hostname type. + */ + if (dns_name_is_single_label(client->hostname)) { + /* it is unclear from RFC 2131 if client should send hostname in + DHCPDISCOVER but dhclient does and so we do as well + */ + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + SD_DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + } else + r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset, + client->hostname); + if (r < 0) + return r; + } + + if (client->vendor_class_identifier) { + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, + strlen(client->vendor_class_identifier), + client->vendor_class_identifier); + if (r < 0) + return r; + } + + if (client->mudurl) { + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + SD_DHCP_OPTION_MUD_URL, + strlen(client->mudurl), + client->mudurl); + if (r < 0) + return r; + } + + if (client->user_class) { + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + SD_DHCP_OPTION_USER_CLASS, + strv_length(client->user_class), + client->user_class); + if (r < 0) + return r; + } + + ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) { + r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0, + j->option, j->length, j->data); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(client->vendor_options)) { + r = dhcp_option_append( + &packet->dhcp, optlen, optoffset, 0, + SD_DHCP_OPTION_VENDOR_SPECIFIC, + ordered_hashmap_size(client->vendor_options), client->vendor_options); + if (r < 0) + return r; + } + + + return 0; +} + static int client_send_discover(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *discover = NULL; size_t optoffset, optlen; - sd_dhcp_option *j; - Iterator i; int r; assert(client); @@ -847,49 +1019,9 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - if (client->hostname) { - /* According to RFC 4702 "clients that send the Client FQDN option in - their messages MUST NOT also send the Host Name option". Just send - one of the two depending on the hostname type. - */ - if (dns_name_is_single_label(client->hostname)) { - /* it is unclear from RFC 2131 if client should send hostname in - DHCPDISCOVER but dhclient does and so we do as well - */ - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); - } else - r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, - client->hostname); - if (r < 0) - return r; - } - - if (client->vendor_class_identifier) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, - strlen(client->vendor_class_identifier), - client->vendor_class_identifier); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_USER_CLASS, - strv_length(client->user_class), - client->user_class); - if (r < 0) - return r; - } - - ORDERED_HASHMAP_FOREACH(j, client->options, i) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - j->option, j->length, j->data); - if (r < 0) - return r; - } + r = client_append_common_discover_request_options(client, discover, &optoffset, optlen); + if (r < 0) + return r; r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); @@ -982,26 +1114,9 @@ static int client_send_request(sd_dhcp_client *client) { return -EINVAL; } - if (client->hostname) { - if (dns_name_is_single_label(client->hostname)) - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); - else - r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, - client->hostname); - if (r < 0) - return r; - } - - if (client->vendor_class_identifier) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, - strlen(client->vendor_class_identifier), - client->vendor_class_identifier); - if (r < 0) - return r; - } + r = client_append_common_discover_request_options(client, request, &optoffset, optlen); + if (r < 0) + return r; r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); @@ -1326,7 +1441,10 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) sd_dhcp_client *client = userdata; DHCP_CLIENT_DONT_DESTROY(client); - client->state = DHCP_STATE_RENEWING; + if (client->lease) + client->state = DHCP_STATE_RENEWING; + else if (client->state != DHCP_STATE_INIT) + client->state = DHCP_STATE_INIT_REBOOT; client->attempt = 0; return client_initialize_time_events(client); @@ -1357,6 +1475,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ lease->next_server = offer->siaddr; lease->address = offer->yiaddr; + if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0) + lease->lifetime = client->fallback_lease_lifetime; + if (lease->address == 0 || lease->server_address == 0 || lease->lifetime == 0) { @@ -1837,13 +1958,13 @@ static int client_receive_message_raw( sd_dhcp_client *client = userdata; _cleanup_free_ DHCPPacket *packet = NULL; - uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control; struct iovec iov = {}; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), + .msg_control = &control, + .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; bool checksum = true; @@ -1865,25 +1986,21 @@ static int client_receive_message_raw( iov = IOVEC_MAKE(packet, buflen); - len = recvmsg(fd, &msg, 0); - if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) - return 0; - - return log_dhcp_client_errno(client, errno, + len = recvmsg_safe(fd, &msg, 0); + if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN)) + return 0; + if (len < 0) + return log_dhcp_client_errno(client, len, "Could not receive message from raw socket: %m"); - } else if ((size_t)len < sizeof(DHCPPacket)) + + if ((size_t) len < sizeof(DHCPPacket)) return 0; - CMSG_FOREACH(cmsg, &msg) - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); - - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - break; - } + cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata))); + if (cmsg) { + struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg); + checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); + } r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) @@ -1898,6 +2015,9 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) { assert_return(client, -EINVAL); assert_return(client->fd >= 0, -EINVAL); + if (!client->lease) + return 0; + client->start_delay = 0; client->attempt = 1; client->state = DHCP_STATE_RENEWING; @@ -2072,8 +2192,10 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { free(client->req_opts); free(client->hostname); free(client->vendor_class_identifier); + free(client->mudurl); client->user_class = strv_free(client->user_class); - ordered_hashmap_free(client->options); + ordered_hashmap_free(client->extra_options); + ordered_hashmap_free(client->vendor_options); return mfree(client); } diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index d072f1c57..0bc5fa321 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -96,37 +96,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { return 0; } +int sd_dhcp_lease_get_servers( + sd_dhcp_lease *lease, + sd_dhcp_lease_server_type what, + const struct in_addr **addr) { + + assert_return(lease, -EINVAL); + assert_return(what >= 0, -EINVAL); + assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->servers[what].size <= 0) + return -ENODATA; + + *addr = lease->servers[what].addr; + return (int) lease->servers[what].size; +} + int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->dns_size <= 0) - return -ENODATA; - - *addr = lease->dns; - return (int) lease->dns_size; + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr); } - int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->ntp_size <= 0) - return -ENODATA; - - *addr = lease->ntp; - return (int) lease->ntp_size; + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr); } - int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->sip_size <= 0) - return -ENODATA; - - *addr = lease->sip; - return (int) lease->sip_size; + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr); +} +int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr); +} +int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr); +} +int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) { + return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr); } int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { @@ -276,9 +279,10 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { free(lease->timezone); free(lease->hostname); free(lease->domainname); - free(lease->dns); - free(lease->ntp); - free(lease->sip); + + for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) + free(lease->servers[i].addr); + free(lease->static_route); free(lease->client_id); free(lease->vendor_specific); @@ -384,7 +388,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { } static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { - assert(option); + assert(option || len == 0); assert(ret); assert(n_ret); @@ -413,33 +417,24 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add } static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { - assert(option); + assert(option || len == 0); assert(ret); assert(n_ret); - if (len <= 0) { + if (len <= 0) + return -EINVAL; + + /* The SIP record is like the other, regular server records, but prefixed with a single "encoding" + * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like + * the other fields */ + + if (option[0] != 1) { /* We only support IP address encoding for now */ *ret = mfree(*ret); *n_ret = 0; - } else { - size_t n_addresses; - struct in_addr *addresses; - int l = len - 1; - - if (l % 4 != 0) - return -EINVAL; - - n_addresses = l / 4; - - addresses = newdup(struct in_addr, option + 1, n_addresses); - if (!addresses) - return -ENOMEM; - - free(*ret); - *ret = addresses; - *n_ret = n_addresses; + return 0; } - return 0; + return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } static int lease_parse_routes( @@ -584,23 +579,41 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: - r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size); if (r < 0) log_debug_errno(r, "Failed to parse DNS server, ignoring: %m"); break; case SD_DHCP_OPTION_NTP_SERVER: - r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size); + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size); if (r < 0) log_debug_errno(r, "Failed to parse NTP server, ignoring: %m"); break; case SD_DHCP_OPTION_SIP_SERVER: - r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size); + r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size); if (r < 0) log_debug_errno(r, "Failed to parse SIP server, ignoring: %m"); break; + case SD_DHCP_OPTION_POP3_SERVER: + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size); + if (r < 0) + log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m"); + break; + + case SD_DHCP_OPTION_SMTP_SERVER: + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size); + if (r < 0) + log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m"); + break; + + case SD_DHCP_OPTION_LPR_SERVER: + r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size); + if (r < 0) + log_debug_errno(r, "Failed to parse LPR server, ignoring: %m"); + break; + case SD_DHCP_OPTION_STATIC_ROUTE: r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated); if (r < 0) @@ -636,7 +649,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void case SD_DHCP_OPTION_HOST_NAME: r = lease_parse_domain(option, len, &lease->hostname); if (r < 0) { - log_debug_errno(r, "Failed to parse host name, ignoring: %m"); + log_debug_errno(r, "Failed to parse hostname, ignoring: %m"); return 0; } @@ -1037,6 +1050,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *dns = NULL, *ntp = NULL, *sip = NULL, + *pop3 = NULL, + *smtp = NULL, + *lpr = NULL, *mtu = NULL, *routes = NULL, *domains = NULL, @@ -1060,12 +1076,15 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "ADDRESS", &address, "ROUTER", &router, "NETMASK", &netmask, - "SERVER_IDENTIFIER", &server_address, + "SERVER_ADDRESS", &server_address, "NEXT_SERVER", &next_server, "BROADCAST", &broadcast, "DNS", &dns, "NTP", &ntp, "SIP", &sip, + "POP3", &pop3, + "SMTP", &smtp, + "LPR", &lpr, "MTU", &mtu, "DOMAINNAME", &lease->domainname, "HOSTNAME", &lease->hostname, @@ -1155,27 +1174,51 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (dns) { - r = deserialize_in_addrs(&lease->dns, dns); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns); if (r < 0) log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns); else - lease->dns_size = r; + lease->servers[SD_DHCP_LEASE_DNS].size = r; } if (ntp) { - r = deserialize_in_addrs(&lease->ntp, ntp); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp); if (r < 0) log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp); else - lease->ntp_size = r; + lease->servers[SD_DHCP_LEASE_NTP].size = r; } if (sip) { - r = deserialize_in_addrs(&lease->sip, sip); + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip); if (r < 0) log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip); else - lease->sip_size = r; + lease->servers[SD_DHCP_LEASE_SIP].size = r; + } + + if (pop3) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3); + if (r < 0) + log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3); + else + lease->servers[SD_DHCP_LEASE_POP3].size = r; + } + + if (smtp) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp); + if (r < 0) + log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp); + else + lease->servers[SD_DHCP_LEASE_SMTP].size = r; + } + + if (lpr) { + r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr); + if (r < 0) + log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr); + else + lease->servers[SD_DHCP_LEASE_LPR].size = r; } if (mtu) { diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index e8b43ba9b..ec20b936b 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -3,6 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ +#include #include #include "sd-dhcp-server.h" @@ -33,7 +34,13 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) { * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address * moreover, the server's own address may be in the pool, and is in that case reserved in order not to * accidentally hand it out */ -int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) { +int sd_dhcp_server_configure_pool( + sd_dhcp_server *server, + const struct in_addr *address, + unsigned char prefixlen, + uint32_t offset, + uint32_t size) { + struct in_addr netmask_addr; be32_t netmask; uint32_t server_off, broadcast_off, size_max; @@ -91,6 +98,9 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres /* Drop any leases associated with the old address range */ hashmap_clear(server->leases_by_client_id); + + if (server->callback) + server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); } return 0; @@ -137,13 +147,14 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { sd_event_unref(server->event); free(server->timezone); - free(server->dns); - free(server->ntp); - free(server->sip); + + for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) + free(server->servers[i].addr); hashmap_free(server->leases_by_client_id); - ordered_hashmap_free(server->raw_option); + ordered_hashmap_free(server->extra_options); + ordered_hashmap_free(server->vendor_options); free(server->bound_leases); return mfree(server); @@ -263,14 +274,14 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, .iov_base = message, .iov_len = len, }; - uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))] = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {}; struct msghdr msg = { .msg_name = &dest, .msg_namelen = sizeof(dest.in), .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), + .msg_control = &control, + .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; struct in_pktinfo *pktinfo; @@ -451,10 +462,24 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, return 0; } -static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, - be32_t address) { +static int server_send_ack( + sd_dhcp_server *server, + DHCPRequest *req, + be32_t address) { + + static const uint8_t option_map[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = { + [SD_DHCP_LEASE_DNS] = SD_DHCP_OPTION_DOMAIN_NAME_SERVER, + [SD_DHCP_LEASE_NTP] = SD_DHCP_OPTION_NTP_SERVER, + [SD_DHCP_LEASE_SIP] = SD_DHCP_OPTION_SIP_SERVER, + [SD_DHCP_LEASE_POP3] = SD_DHCP_OPTION_POP3_SERVER, + [SD_DHCP_LEASE_SMTP] = SD_DHCP_OPTION_SMTP_SERVER, + [SD_DHCP_LEASE_LPR] = SD_DHCP_OPTION_LPR_SERVER, + }; + _cleanup_free_ DHCPPacket *packet = NULL; be32_t lease_time; + sd_dhcp_option *j; + Iterator i; size_t offset; int r; @@ -483,32 +508,19 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, return r; } - if (server->n_dns > 0) { + for (sd_dhcp_lease_server_type k = 0; k < _SD_DHCP_LEASE_SERVER_TYPE_MAX; k++) { + + if (server->servers[k].size <= 0) + continue; + r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER, - sizeof(struct in_addr) * server->n_dns, server->dns); + option_map[k], + sizeof(struct in_addr) * server->servers[k].size, server->servers[k].addr); if (r < 0) return r; } - if (server->n_ntp > 0) { - r = dhcp_option_append( - &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_NTP_SERVER, - sizeof(struct in_addr) * server->n_ntp, server->ntp); - if (r < 0) - return r; - } - - if (server->n_sip > 0) { - r = dhcp_option_append( - &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_SIP_SERVER, - sizeof(struct in_addr) * server->n_sip, server->sip); - if (r < 0) - return r; - } if (server->timezone) { r = dhcp_option_append( @@ -519,11 +531,18 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, return r; } - if (!ordered_hashmap_isempty(server->raw_option)) { + ORDERED_HASHMAP_FOREACH(j, server->extra_options, i) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + j->option, j->length, j->data); + if (r < 0) + return r; + } + + if (!ordered_hashmap_isempty(server->vendor_options)) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, SD_DHCP_OPTION_VENDOR_SPECIFIC, - ordered_hashmap_size(server->raw_option), server->raw_option); + ordered_hashmap_size(server->vendor_options), server->vendor_options); if (r < 0) return r; } @@ -883,6 +902,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, hashmap_put(server->leases_by_client_id, &lease->client_id, lease); + if (server->callback) + server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); + return DHCP_ACK; } @@ -919,6 +941,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, server->bound_leases[pool_offset] = NULL; hashmap_remove(server->leases_by_client_id, existing_lease); dhcp_lease_free(existing_lease); + + if (server->callback) + server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); } return 0; @@ -930,14 +955,14 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, static int server_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { _cleanup_free_ DHCPMessage *message = NULL; - uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control; sd_dhcp_server *server = userdata; struct iovec iov = {}; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), + .msg_control = &control, + .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; ssize_t buflen, len; @@ -955,14 +980,12 @@ static int server_receive_message(sd_event_source *s, int fd, iov = IOVEC_MAKE(message, buflen); - len = recvmsg(fd, &msg, 0); - if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) - return 0; - - return -errno; - } - if ((size_t)len < sizeof(DHCPMessage)) + len = recvmsg_safe(fd, &msg, 0); + if (IN_SET(len, -EAGAIN, -EINTR)) + return 0; + if (len < 0) + return len; + if ((size_t) len < sizeof(DHCPMessage)) return 0; CMSG_FOREACH(cmsg, &msg) { @@ -1093,82 +1116,52 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) { return 1; } -int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) { - assert_return(server, -EINVAL); - assert_return(dns || n <= 0, -EINVAL); +int sd_dhcp_server_set_servers( + sd_dhcp_server *server, + sd_dhcp_lease_server_type what, + const struct in_addr addresses[], + size_t n_addresses) { - if (server->n_dns == n && - memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0) + struct in_addr *c = NULL; + + assert_return(server, -EINVAL); + assert_return(addresses || n_addresses == 0, -EINVAL); + assert_return(what >= 0, -EINVAL); + assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL); + + if (server->servers[what].size == n_addresses && + memcmp(server->servers[what].addr, addresses, sizeof(struct in_addr) * n_addresses) == 0) return 0; - if (n <= 0) { - server->dns = mfree(server->dns); - server->n_dns = 0; - } else { - struct in_addr *c; - - c = newdup(struct in_addr, dns, n); + if (n_addresses > 0) { + c = newdup(struct in_addr, addresses, n_addresses); if (!c) return -ENOMEM; - - free(server->dns); - server->dns = c; - server->n_dns = n; } + free(server->servers[what].addr); + server->servers[what].addr = c; + server->servers[what].size = n_addresses; return 1; } -int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) { - assert_return(server, -EINVAL); - assert_return(ntp || n <= 0, -EINVAL); - - if (server->n_ntp == n && - memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0) - return 0; - - if (n <= 0) { - server->ntp = mfree(server->ntp); - server->n_ntp = 0; - } else { - struct in_addr *c; - - c = newdup(struct in_addr, ntp, n); - if (!c) - return -ENOMEM; - - free(server->ntp); - server->ntp = c; - server->n_ntp = n; - } - - return 1; +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_DNS, dns, n); } - -int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n) { - assert_return(server, -EINVAL); - assert_return(sip || n <= 0, -EINVAL); - - if (server->n_sip == n && - memcmp(server->sip, sip, sizeof(struct in_addr) * n) == 0) - return 0; - - if (n <= 0) { - server->sip = mfree(server->sip); - server->n_sip = 0; - } else { - struct in_addr *c; - - c = newdup(struct in_addr, sip, n); - if (!c) - return -ENOMEM; - - free(server->sip); - server->sip = c; - server->n_sip = n; - } - - return 1; +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_NTP, ntp, n); +} +int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SIP, sip, n); +} +int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_POP3, pop3, n); +} +int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SMTP, smtp, n); +} +int sd_dhcp_server_set_lpr(sd_dhcp_server *server, const struct in_addr lpr[], size_t n) { + return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_LPR, lpr, n); } int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) { @@ -1188,11 +1181,29 @@ int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) { assert_return(server, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&server->raw_option, &dhcp_option_hash_ops); + r = ordered_hashmap_ensure_allocated(&server->extra_options, &dhcp_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(server->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; + + sd_dhcp_option_ref(v); + return 0; +} + +int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v) { + int r; + + assert_return(server, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&server->vendor_options, &dhcp_option_hash_ops); if (r < 0) return -ENOMEM; - r = ordered_hashmap_put(server->raw_option, v, v); + r = ordered_hashmap_put(server->vendor_options, v, v); if (r < 0) return r; @@ -1200,3 +1211,12 @@ int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) { return 1; } + +int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata) { + assert_return(server, -EINVAL); + + server->callback = cb; + server->callback_userdata = userdata; + + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index eac2e725c..5cdb82bc6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -18,6 +18,7 @@ #include "dns-domain.h" #include "event-util.h" #include "fd-util.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" #include "network-internal.h" @@ -25,6 +26,7 @@ #include "socket-util.h" #include "string-table.h" #include "util.h" +#include "web-util.h" #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN @@ -65,6 +67,9 @@ struct sd_dhcp6_client { size_t req_opts_allocated; 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; @@ -76,6 +81,8 @@ struct sd_dhcp6_client { size_t duid_len; usec_t information_request_time_usec; usec_t information_refresh_time_usec; + OrderedHashmap *extra_options; + OrderedHashmap *vendor_options; }; static const uint16_t default_req_opts[] = { @@ -104,12 +111,29 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { 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_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); @@ -135,7 +159,7 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); - assert_return(ifindex >= -1, -EINVAL); + assert_return(ifindex > 0, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); client->ifindex = ifindex; @@ -203,6 +227,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint( return 0; } +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); + + r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->vendor_options, v, v); + if (r < 0) + return r; + + sd_dhcp6_option_ref(v); + + return 1; +} + static int client_ensure_duid(sd_dhcp6_client *client) { if (client->duid_len != 0) return 0; @@ -233,7 +276,8 @@ static int dhcp6_client_set_duid_internal( r = dhcp_validate_duid_len(duid_type, duid_len, false); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m"); - log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type); + + log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type); } client->duid.type = htobe16(duid_type); @@ -288,6 +332,48 @@ 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) { + _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; + const char *v; + int r; + + assert_return(client, -EINVAL); + assert_return(client->duid_len > 0, -ENODATA); + + v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); + if (v) { + s = strdup(v); + if (!s) + return -ENOMEM; + } else { + r = asprintf(&s, "%0x", client->duid.type); + if (r < 0) + return -ENOMEM; + } + + t = hexmem(&client->duid.raw.data, client->duid_len); + if (!t) + return -ENOMEM; + + p = strjoin(s, ":", t); + if (!p) + return -ENOMEM; + + *duid = TAKE_PTR(p); + + return 0; +} + int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -299,6 +385,18 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { 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.ia_na.id); + + return 0; +} + int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { @@ -337,18 +435,8 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - switch(option) { - - case SD_DHCP6_OPTION_DNS_SERVERS: - case SD_DHCP6_OPTION_DOMAIN_LIST: - case SD_DHCP6_OPTION_SNTP_SERVERS: - case SD_DHCP6_OPTION_NTP_SERVER: - case SD_DHCP6_OPTION_RAPID_COMMIT: - break; - - default: + if (option <= 0 || option >= UINT8_MAX) return -EINVAL; - } for (t = 0; t < client->req_opts_len; t++) if (client->req_opts[t] == htobe16(option)) @@ -363,6 +451,60 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } +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(mudurl, -EINVAL); + assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); + assert_return(http_url_is_valid(mudurl), -EINVAL); + + return free_and_strdup(&client->mudurl, mudurl); +} + +int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) { + _cleanup_strv_free_ char **s = NULL; + char **p; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + assert_return(user_class, -EINVAL); + + STRV_FOREACH(p, user_class) + if (strlen(*p) > UINT16_MAX) + return -ENAMETOOLONG; + + s = strv_copy(user_class); + if (!s) + return -ENOMEM; + + client->user_class = TAKE_PTR(s); + + return 0; +} + +int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) { + _cleanup_strv_free_ char **s = NULL; + char **p; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(vendor_class, -EINVAL); + + STRV_FOREACH(p, vendor_class) + if (strlen(*p) > UINT8_MAX) + return -ENAMETOOLONG; + + s = strv_copy(vendor_class); + if (!s) + return -ENOMEM; + + client->vendor_class = TAKE_PTR(s); + + return 0; +} + int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { assert_return(client, -EINVAL); assert_return(delegation, -EINVAL); @@ -417,6 +559,24 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { return 0; } +int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { + int r; + + assert_return(client, -EINVAL); + assert_return(v, -EINVAL); + + r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + if (r < 0) + return r; + + sd_dhcp6_option_ref(v); + return 0; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -462,7 +622,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { _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; + Iterator i; uint8_t *opt; int r; usec_t elapsed_usec; @@ -484,6 +646,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { case DHCP6_STATE_INFORMATION_REQUEST: message->type = DHCP6_INFORMATION_REQUEST; + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, + SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl), + client->mudurl); + if (r < 0) + return r; + } + break; case DHCP6_STATE_SOLICITATION: @@ -507,6 +677,33 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, + SD_DHCP6_OPTION_MUD_URL, 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, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix); if (r < 0) @@ -545,6 +742,32 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, + SD_DHCP6_OPTION_MUD_URL, 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, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) @@ -571,6 +794,32 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, + SD_DHCP6_OPTION_MUD_URL, 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, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); if (r < 0) @@ -610,6 +859,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, i) { + 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) @@ -837,10 +1092,11 @@ static int client_parse_message( size_t len, sd_dhcp6_lease *lease) { + uint16_t ia_na_status = 0, ia_pd_status = 0; uint32_t lt_t1 = ~0, lt_t2 = ~0; + usec_t irt = IRT_DEFAULT; bool clientid = false; size_t pos = 0; - usec_t irt = IRT_DEFAULT; int r; assert(client); @@ -854,8 +1110,8 @@ static int client_parse_message( DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; be32_t iaid_lease; + int status; uint8_t *optval; - int status; if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; @@ -932,10 +1188,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -960,10 +1221,15 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(option, &lease->pd); + r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status); if (r < 0 && r != -ENOMSG) return r; + if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { + pos += offsetof(DHCP6Option, data) + optlen; + continue; + } + r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -1027,6 +1293,11 @@ static int client_parse_message( pos += offsetof(DHCP6Option, data) + optlen; } + if (ia_na_status > 0 && ia_pd_status > 0) { + log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring."); + return -EINVAL; + } + if (!clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); @@ -1218,7 +1489,7 @@ static int client_receive_message( break; } - _fallthrough_; /* for Soliciation Rapid Commit option check */ + _fallthrough_; /* for Solicitation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: @@ -1521,6 +1792,12 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { free(client->req_opts); free(client->fqdn); + free(client->mudurl); + + ordered_hashmap_free(client->extra_options); + strv_free(client->user_class); + strv_free(client->vendor_class); + return mfree(client); } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 8aebb53c8..4eee10ea8 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -213,7 +213,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { return 0; } -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { assert_return(lease, -EINVAL); assert_return(addrs, -EINVAL); @@ -341,7 +341,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) } int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs) { + const struct in6_addr **addrs) { assert_return(lease, -EINVAL); assert_return(addrs, -EINVAL); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index aa1ece41d..4f4d9ebe8 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -252,12 +252,14 @@ static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) { return r; } - return 0; + return 1; } int sd_ipv4ll_start(sd_ipv4ll *ll) { assert_return(ll, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + if (sd_ipv4ll_is_running(ll)) + return 0; return ipv4ll_start_internal(ll, true); } diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 0d65b3f5b..6163cf169 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -77,6 +77,12 @@ _public_ sd_event *sd_radv_get_event(sd_radv *ra) { return ra->event; } +_public_ int sd_radv_is_running(sd_radv *ra) { + assert_return(ra, false); + + return ra->state != SD_RADV_STATE_IDLE; +} + static void radv_reset(sd_radv *ra) { assert(ra); @@ -415,7 +421,7 @@ _public_ int sd_radv_start(sd_radv *ra) { _public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) { assert_return(ra, -EINVAL); - assert_return(ifindex >= -1, -EINVAL); + assert_return(ifindex > 0, -EINVAL); if (ra->state != SD_RADV_STATE_IDLE) return -EBUSY; @@ -572,6 +578,15 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) { cur = p; + /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */ + if (ra->ra_sent > 0) { + r = radv_send(ra, NULL, ra->lifetime); + if (r < 0) + log_radv_errno(r, "Unable to send Router Advertisement for added prefix: %m"); + else + log_radv("Sent Router Advertisement for added prefix"); + } + update: r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) @@ -680,6 +695,15 @@ _public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int return 0; } + /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */ + if (ra->ra_sent > 0) { + r = radv_send(ra, NULL, ra->lifetime); + if (r < 0) + log_radv_errno(r, "Unable to send Router Advertisement for added route prefix: %m"); + else + log_radv("Sent Router Advertisement for added route prefix"); + } + update: r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) @@ -827,6 +851,18 @@ _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr return 0; } +_public_ 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); + + *ret_in6_addr = p->opt.in6_addr; + *ret_prefixlen = p->opt.prefixlen; + + return 0; +} + _public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) { assert_return(p, -EINVAL); diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 4e9b388a4..8f2f4462b 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -4,10 +4,11 @@ ***/ #include +#include +#include #include #include #include -#include #include "sd-dhcp-client.h" #include "sd-event.h" @@ -257,7 +258,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const } int dhcp_network_bind_raw_socket( - int index, + int ifindex, union sockaddr_union *link, uint32_t id, const uint8_t *addr, size_t addr_len, diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 56bd690cb..086f8b5fd 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -1,4 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + #include +#include #include #include #include diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index ea998939b..a45d98f66 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -4,6 +4,7 @@ ***/ #include +#include #include "sd-dhcp-server.h" #include "sd-event.h" diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 4b89b320b..7af7d670b 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -30,7 +30,7 @@ static struct ether_addr mac_addr = { static sd_event_source *hangcheck; static int test_dhcp_fd[2]; -static int test_index = 42; +static int test_ifindex = 42; static int test_client_message_num; static be32_t test_iaid = 0; static uint8_t test_duid[14] = { }; @@ -48,7 +48,7 @@ static int test_client_basic(sd_event *e) { 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) == 0); + 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, @@ -61,12 +61,12 @@ static int test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_fqdn(client, "~host") == -EINVAL); assert_se(sd_dhcp6_client_set_fqdn(client, "~host.domain") == -EINVAL); - assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL); + assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == 0); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); + assert_se(sd_dhcp6_client_set_request_option(client, 10) == 0); assert_se(sd_dhcp6_client_set_information_request(client, 1) >= 0); v = 0; @@ -247,17 +247,17 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); - assert_se(r == -EINVAL); + r = dhcp6_option_parse_ia(option, &ia, NULL); + assert_se(r == 0); assert_se(ia.addresses == NULL); option->len = htobe16(17); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r == -ENOBUFS); assert_se(ia.addresses == NULL); option->len = htobe16(sizeof(DHCP6Option)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r == -ENOBUFS); assert_se(ia.addresses == NULL); @@ -265,7 +265,7 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r >= 0); assert_se(ia.addresses == NULL); @@ -273,7 +273,7 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &ia); + r = dhcp6_option_parse_ia(option, &ia, NULL); assert_se(r >= 0); assert_se(ia.addresses != NULL); dhcp6_lease_free_ia(&ia); @@ -282,8 +282,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &pd); - assert_se(r == 0); + r = dhcp6_option_parse_ia(option, &pd, NULL); + assert_se(r >= 0); assert_se(pd.addresses != NULL); assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); @@ -294,8 +294,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option *)option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(option, &pd); - assert_se(r == 0); + r = dhcp6_option_parse_ia(option, &pd, NULL); + assert_se(r >= 0); assert_se(pd.addresses != NULL); dhcp6_lease_free_ia(&pd); @@ -364,15 +364,15 @@ static int 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; - be32_t val; + uint32_t lt_pref, lt_valid; + bool opt_clientid = false; + const struct in6_addr *addrs; uint8_t preference = 255; struct in6_addr addr; - uint32_t lt_pref, lt_valid; - int r; - uint8_t *opt; - bool opt_clientid = false; - struct in6_addr *addrs; char **domains; + uint8_t *opt; + int r; + be32_t val; log_debug("/* %s */", __func__); @@ -410,7 +410,7 @@ static int test_advertise_option(sd_event *e) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0); break; @@ -518,7 +518,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp6_lease *lease; - struct in6_addr *addrs; + const struct in6_addr *addrs; char **domains; log_debug("/* %s */", __func__); @@ -563,12 +563,12 @@ static int test_client_send_reply(DHCP6Message *request) { static int test_client_verify_request(DHCP6Message *request, size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - size_t pos = 0; bool found_clientid = false, found_iana = false, found_serverid = false, found_elapsed_time = false, found_fqdn = false; - struct in6_addr addr; - be32_t val; uint32_t lt_pref, lt_valid; + struct in6_addr addr; + size_t pos = 0; + be32_t val; log_debug("/* %s */", __func__); @@ -606,7 +606,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(!dhcp6_option_parse_ia(option, &lease->ia)); + assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0); break; @@ -744,7 +744,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp6_lease *lease; - struct in6_addr *addrs; + 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; @@ -877,8 +877,8 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, return len; } -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - assert_se(index == test_index); +int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { + assert_se(ifindex == test_ifindex); if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0) return -errno; @@ -899,7 +899,7 @@ static int test_client_solicit(sd_event *e) { assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 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); diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index 2f319bf7d..310b658e1 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -78,7 +78,7 @@ int arp_send_announcement(int fd, int ifindex, return arp_network_send_raw_socket(fd, ifindex, &ea); } -int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { +int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) return -errno; @@ -159,10 +159,10 @@ static void test_basic_request(sd_event *e) { assert_se(sd_ipv4ll_start(ll) == -EINVAL); assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0); - assert_se(sd_ipv4ll_start(ll) == 0); + assert_se(sd_ipv4ll_start(ll) == 1); sd_event_run(e, (uint64_t) -1); - assert_se(sd_ipv4ll_start(ll) == -EBUSY); + assert_se(sd_ipv4ll_start(ll) == 0); assert_se(sd_ipv4ll_is_running(ll)); diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index 7c6c4663f..d759ec03a 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -159,8 +159,8 @@ static void test_radv(void) { assert_se(ra); assert_se(sd_radv_set_ifindex(NULL, 0) < 0); - assert_se(sd_radv_set_ifindex(ra, 0) >= 0); - assert_se(sd_radv_set_ifindex(ra, -1) >= 0); + assert_se(sd_radv_set_ifindex(ra, 0) < 0); + assert_se(sd_radv_set_ifindex(ra, -1) < 0); assert_se(sd_radv_set_ifindex(ra, -2) < 0); assert_se(sd_radv_set_ifindex(ra, 42) >= 0); @@ -219,12 +219,12 @@ static void test_radv(void) { assert_se(!ra); } -int icmp6_bind_router_solicitation(int index) { +int icmp6_bind_router_solicitation(int ifindex) { return -ENOSYS; } -int icmp6_bind_router_advertisement(int index) { - assert_se(index == 42); +int icmp6_bind_router_advertisement(int ifindex) { + assert_se(ifindex == 42); return test_fd[1]; } diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index c8ee1ec31..3f46c8d0c 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -174,8 +174,8 @@ static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, return 0; } -int icmp6_bind_router_solicitation(int index) { - assert_se(index == 42); +int icmp6_bind_router_solicitation(int ifindex) { + assert_se(ifindex == 42); if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0) return -errno; @@ -183,8 +183,7 @@ int icmp6_bind_router_solicitation(int index) { return test_fd[0]; } -int icmp6_bind_router_advertisement(int index) { - +int icmp6_bind_router_advertisement(int ifindex) { return -ENOSYS; } @@ -218,7 +217,7 @@ static int send_ra(uint8_t flags) { advertisement[5] = flags; assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == - sizeof(advertisement)); + sizeof(advertisement)); if (verbose) printf(" sent RA with flag 0x%02x\n", flags); @@ -292,11 +291,12 @@ static void test_rs(void) { assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0); assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), - time_now + 2 *USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); + time_now + 30 * USEC_PER_SEC, 0, + test_rs_hangcheck, NULL) >= 0); assert_se(sd_ndisc_stop(nd) >= 0); assert_se(sd_ndisc_start(nd) >= 0); + assert_se(sd_ndisc_start(nd) >= 0); assert_se(sd_ndisc_stop(nd) >= 0); assert_se(sd_ndisc_start(nd) >= 0); @@ -393,8 +393,8 @@ static void test_timeout(void) { assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), - time_now + 2U * USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); + time_now + 30 * USEC_PER_SEC, 0, + test_rs_hangcheck, NULL) >= 0); assert_se(sd_ndisc_start(nd) >= 0); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 8b6ebbcf8..1e654b49e 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -697,3 +697,27 @@ global: sd_event_source_send_child_signal; sd_journal_open_namespace; } LIBSYSTEMD_243; + +LIBSYSTEMD_246 { +global: + sd_bus_interface_name_is_valid; + sd_bus_service_name_is_valid; + sd_bus_member_name_is_valid; + sd_bus_object_path_is_valid; + + sd_bus_call_methodv; + sd_bus_call_method_asyncv; + sd_bus_emit_signalv; + sd_bus_reply_method_errnofv; + sd_bus_reply_method_errorfv; + sd_bus_reply_method_returnv; + sd_bus_set_propertyv; + + sd_path_lookup; + sd_path_lookup_strv; + + sd_notify_barrier; + + sd_journal_enumerate_available_data; + sd_journal_enumerate_available_unique; +} LIBSYSTEMD_245; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 174f1228a..dc9a2fdc3 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -120,6 +120,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, EBADSLT), SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED, ENOANO), SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL), + SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, EMEDIUMTYPE), + SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_ACTION_TIMEOUT, ENOSTR), SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED, EOWNERDEAD), SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN, ENOLCK), SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, ETOOMANYREFS), @@ -134,6 +136,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED, ENOEXEC), SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS), SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS), + SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED), SD_BUS_ERROR_MAP_END }; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index e5f92b9ec..ae8054388 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -29,6 +29,7 @@ #define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull" #define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean" #define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy" +#define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" @@ -99,6 +100,8 @@ #define BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN "org.freedesktop.home1.BadPasswordAndNoToken" #define BUS_ERROR_TOKEN_PIN_NEEDED "org.freedesktop.home1.TokenPinNeeded" #define BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED "org.freedesktop.home1.TokenProtectedAuthenticationPathNeeded" +#define BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED "org.freedesktop.home1.TokenUserPresenceNeeded" +#define BUS_ERROR_TOKEN_ACTION_TIMEOUT "org.freedesktop.home1.TokenActionTimeout" #define BUS_ERROR_TOKEN_PIN_LOCKED "org.freedesktop.home1.TokenPinLocked" #define BUS_ERROR_TOKEN_BAD_PIN "org.freedesktop.home1.BadPin" #define BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT "org.freedesktop.home1.BadPinFewTriesLeft" @@ -114,5 +117,6 @@ #define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace" #define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations" #define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit" +#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate" BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 4ec061644..a5672a831 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -10,12 +10,12 @@ #include "bus-util.h" #include "string-util.h" -_public_ int sd_bus_emit_signal( +_public_ int sd_bus_emit_signalv( sd_bus *bus, const char *path, const char *interface, const char *member, - const char *types, ...) { + const char *types, va_list ap) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int r; @@ -32,11 +32,7 @@ _public_ int sd_bus_emit_signal( return r; if (!isempty(types)) { - va_list ap; - - va_start(ap, types); r = sd_bus_message_appendv(m, types, ap); - va_end(ap); if (r < 0) return r; } @@ -44,7 +40,24 @@ _public_ int sd_bus_emit_signal( return sd_bus_send(bus, m, NULL); } -_public_ int sd_bus_call_method_async( +_public_ int sd_bus_emit_signal( + sd_bus *bus, + const char *path, + const char *interface, + const char *member, + const char *types, ...) { + + va_list ap; + int r; + + va_start(ap, types); + r = sd_bus_emit_signalv(bus, path, interface, member, types, ap); + va_end(ap); + + return r; +} + +_public_ int sd_bus_call_method_asyncv( sd_bus *bus, sd_bus_slot **slot, const char *destination, @@ -53,7 +66,7 @@ _public_ int sd_bus_call_method_async( const char *member, sd_bus_message_handler_t callback, void *userdata, - const char *types, ...) { + const char *types, va_list ap) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int r; @@ -70,11 +83,7 @@ _public_ int sd_bus_call_method_async( return r; if (!isempty(types)) { - va_list ap; - - va_start(ap, types); r = sd_bus_message_appendv(m, types, ap); - va_end(ap); if (r < 0) return r; } @@ -82,7 +91,28 @@ _public_ int sd_bus_call_method_async( return sd_bus_call_async(bus, slot, m, callback, userdata, 0); } -_public_ int sd_bus_call_method( +_public_ int sd_bus_call_method_async( + sd_bus *bus, + sd_bus_slot **slot, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + void *userdata, + const char *types, ...) { + + va_list ap; + int r; + + va_start(ap, types); + r = sd_bus_call_method_asyncv(bus, slot, destination, path, interface, member, callback, userdata, types, ap); + va_end(ap); + + return r; +} + +_public_ int sd_bus_call_methodv( sd_bus *bus, const char *destination, const char *path, @@ -90,12 +120,13 @@ _public_ int sd_bus_call_method( const char *member, sd_bus_error *error, sd_bus_message **reply, - const char *types, ...) { + const char *types, va_list ap) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); if (!BUS_IS_OPEN(bus->state)) { @@ -108,11 +139,7 @@ _public_ int sd_bus_call_method( goto fail; if (!isempty(types)) { - va_list ap; - - va_start(ap, types); r = sd_bus_message_appendv(m, types, ap); - va_end(ap); if (r < 0) goto fail; } @@ -123,10 +150,30 @@ fail: return sd_bus_error_set_errno(error, r); } -_public_ int sd_bus_reply_method_return( - sd_bus_message *call, +_public_ int sd_bus_call_method( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, const char *types, ...) { + va_list ap; + int r; + + va_start(ap, types); + r = sd_bus_call_methodv(bus, destination, path, interface, member, error, reply, types, ap); + va_end(ap); + + return r; +} + +_public_ int sd_bus_reply_method_returnv( + sd_bus_message *call, + const char *types, va_list ap) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int r; @@ -147,11 +194,7 @@ _public_ int sd_bus_reply_method_return( return r; if (!isempty(types)) { - va_list ap; - - va_start(ap, types); r = sd_bus_message_appendv(m, types, ap); - va_end(ap); if (r < 0) return r; } @@ -159,6 +202,20 @@ _public_ int sd_bus_reply_method_return( return sd_bus_send(call->bus, m, NULL); } +_public_ int sd_bus_reply_method_return( + sd_bus_message *call, + const char *types, ...) { + + va_list ap; + int r; + + va_start(ap, types); + r = sd_bus_reply_method_returnv(call, types, ap); + va_end(ap); + + return r; +} + _public_ int sd_bus_reply_method_error( sd_bus_message *call, const sd_bus_error *e) { @@ -186,14 +243,13 @@ _public_ int sd_bus_reply_method_error( return sd_bus_send(call->bus, m, NULL); } -_public_ int sd_bus_reply_method_errorf( +_public_ int sd_bus_reply_method_errorfv( sd_bus_message *call, const char *name, const char *format, - ...) { + va_list ap) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - va_list ap; assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); @@ -207,13 +263,27 @@ _public_ int sd_bus_reply_method_errorf( if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) return 0; - va_start(ap, format); bus_error_setfv(&error, name, format, ap); - va_end(ap); return sd_bus_reply_method_error(call, &error); } +_public_ int sd_bus_reply_method_errorf( + sd_bus_message *call, + const char *name, + const char *format, + ...) { + + va_list ap; + int r; + + va_start(ap, format); + r = sd_bus_reply_method_errorfv(call, name, format, ap); + va_end(ap); + + return r; +} + _public_ int sd_bus_reply_method_errno( sd_bus_message *call, int error, @@ -241,14 +311,13 @@ _public_ int sd_bus_reply_method_errno( return sd_bus_reply_method_error(call, &berror); } -_public_ int sd_bus_reply_method_errnof( +_public_ int sd_bus_reply_method_errnofv( sd_bus_message *call, int error, const char *format, - ...) { + va_list ap) { _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - va_list ap; assert_return(call, -EINVAL); assert_return(call->sealed, -EPERM); @@ -262,13 +331,27 @@ _public_ int sd_bus_reply_method_errnof( if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) return 0; - va_start(ap, format); sd_bus_error_set_errnofv(&berror, error, format, ap); - va_end(ap); return sd_bus_reply_method_error(call, &berror); } +_public_ int sd_bus_reply_method_errnof( + sd_bus_message *call, + int error, + const char *format, + ...) { + + va_list ap; + int r; + + va_start(ap, format); + r = sd_bus_reply_method_errnofv(call, error, format, ap); + va_end(ap); + + return r; +} + _public_ int sd_bus_get_property( sd_bus *bus, const char *destination, @@ -283,6 +366,7 @@ _public_ int sd_bus_get_property( int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); bus_assert_return(member_name_is_valid(member), -EINVAL, error); bus_assert_return(reply, -EINVAL, error); @@ -294,7 +378,10 @@ _public_ int sd_bus_get_property( goto fail; } - r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member); + r = sd_bus_call_method(bus, destination, path, + "org.freedesktop.DBus.Properties", "Get", + error, &rep, + "ss", strempty(interface), member); if (r < 0) return r; @@ -324,6 +411,7 @@ _public_ int sd_bus_get_property_trivial( int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); bus_assert_return(member_name_is_valid(member), -EINVAL, error); bus_assert_return(bus_type_is_trivial(type), -EINVAL, error); @@ -368,6 +456,7 @@ _public_ int sd_bus_get_property_string( int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); bus_assert_return(member_name_is_valid(member), -EINVAL, error); bus_assert_return(ret, -EINVAL, error); @@ -416,6 +505,7 @@ _public_ int sd_bus_get_property_strv( int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); bus_assert_return(member_name_is_valid(member), -EINVAL, error); bus_assert_return(ret, -EINVAL, error); @@ -444,20 +534,20 @@ fail: return sd_bus_error_set_errno(error, r); } -_public_ int sd_bus_set_property( +_public_ int sd_bus_set_propertyv( sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *error, - const char *type, ...) { + const char *type, va_list ap) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - va_list ap; int r; bus_assert_return(bus, -EINVAL, error); + bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error); bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); bus_assert_return(member_name_is_valid(member), -EINVAL, error); bus_assert_return(signature_is_single(type, false), -EINVAL, error); @@ -480,9 +570,7 @@ _public_ int sd_bus_set_property( if (r < 0) goto fail; - va_start(ap, type); r = sd_bus_message_appendv(m, type, ap); - va_end(ap); if (r < 0) goto fail; @@ -496,6 +584,25 @@ fail: return sd_bus_error_set_errno(error, r); } +_public_ int sd_bus_set_property( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + const char *type, ...) { + + va_list ap; + int r; + + va_start(ap, type); + r = sd_bus_set_propertyv(bus, destination, path, interface, member, error, type, ap); + va_end(ap); + + return r; +} + _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) { sd_bus_creds *c; diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 908b9e75b..2740be922 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -650,16 +650,15 @@ _public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { } static int has_cap(sd_bus_creds *c, size_t offset, int capability) { - unsigned long lc; size_t sz; assert(c); assert(capability >= 0); assert(c->capability); - lc = cap_last_cap(); + unsigned lc = cap_last_cap(); - if ((unsigned long) capability > lc) + if ((unsigned) capability > lc) return 0; /* If the last cap is 63, then there are 64 caps defined, and we need 2 entries á 32bit hence. * diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index caab5e5eb..94107c297 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -56,7 +56,7 @@ _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) { if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) { fprintf(f, - "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, + "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u", m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", @@ -69,8 +69,7 @@ _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) { m->header->endian, m->header->flags, - m->header->version, - m->priority); + m->header->version); /* Display synthetic message serial number in a more readable * format than (uint32_t) -1 */ diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index d5f8c6db6..5c3e955c2 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -314,13 +314,9 @@ char *bus_address_escape(const char *v) { int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) { assert(m); - if (r < 0) { + if (sd_bus_error_is_set(error) || r < 0) { if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) sd_bus_reply_method_errno(m, r, error); - - } else if (sd_bus_error_is_set(error)) { - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_reply_method_error(m, error); } else return r; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 352a419e2..ef2c3dbc4 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -43,7 +43,7 @@ struct match_callback { unsigned last_iteration; - /* Don't dispatch this slot with with messages that arrived in any iteration before or at the this + /* Don't dispatch this slot with messages that arrived in any iteration before or at the this * one. We use this to ensure that matches don't apply "retroactively" and thus can confuse the * caller: matches will only match incoming messages from the moment on the match was installed. */ uint64_t after; @@ -354,6 +354,7 @@ bool interface_name_is_valid(const char *p) _pure_; bool service_name_is_valid(const char *p) _pure_; bool member_name_is_valid(const char *p) _pure_; bool object_path_is_valid(const char *p) _pure_; + char *object_path_startswith(const char *a, const char *b) _pure_; bool namespace_complex_pattern(const char *pattern, const char *value) _pure_; diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index e8934489b..734abcf3f 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -10,11 +10,69 @@ #include "memory-util.h" #include "string-util.h" +#define BUS_INTROSPECT_DOCTYPE \ + "\n" + +#define BUS_INTROSPECT_INTERFACE_PEER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + int introspect_begin(struct introspect *i, bool trusted) { assert(i); - zero(*i); - i->trusted = trusted; + *i = (struct introspect) { + .trusted = trusted, + }; i->f = open_memstream_unlocked(&i->introspection, &i->size); if (!i->f) @@ -39,12 +97,27 @@ int introspect_write_default_interfaces(struct introspect *i, bool object_manage return 0; } +static int set_interface_name(struct introspect *intro, const char *interface_name) { + if (streq_ptr(intro->interface_name, interface_name)) + return 0; + + if (intro->interface_name) + fputs(" \n", intro->f); + + if (interface_name) + fprintf(intro->f, " \n", interface_name); + + return free_and_strdup(&intro->interface_name, interface_name); +} + int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { char *node; assert(i); assert(prefix); + assert_se(set_interface_name(i, NULL) >= 0); + while ((node = set_steal_first(s))) { const char *e; @@ -114,13 +187,23 @@ static int introspect_write_arguments(struct introspect *i, const char *signatur } } -int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { +int introspect_write_interface( + struct introspect *i, + const char *interface_name, + const sd_bus_vtable *v) { + const sd_bus_vtable *vtable = v; const char *names = ""; + int r; assert(i); + assert(interface_name); assert(v); + r = set_interface_name(i, interface_name); + if (r < 0) + return r; + for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { /* Ignore methods, signals and properties that are @@ -177,6 +260,8 @@ int introspect_finish(struct introspect *i, char **ret) { assert(i); + assert_se(set_interface_name(i, NULL) >= 0); + fputs("\n", i->f); r = fflush_and_check(i->f); @@ -195,5 +280,6 @@ void introspect_free(struct introspect *i) { /* Normally introspect_finish() does all the work, this is just a backup for error paths */ safe_fclose(i->f); + free(i->interface_name); free(i->introspection); } diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h index ccbb543d0..19d39923e 100644 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ b/src/libsystemd/sd-bus/bus-introspect.h @@ -9,6 +9,7 @@ struct introspect { FILE *f; + char *interface_name; char *introspection; size_t size; bool trusted; @@ -17,6 +18,9 @@ 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_interface(struct introspect *i, const sd_bus_vtable *v); +int introspect_write_interface( + struct introspect *i, + const char *interface_name, + const sd_bus_vtable *v); int introspect_finish(struct introspect *i, char **ret); void introspect_free(struct introspect *i); diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 8b00a49ed..55e35cd90 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -46,7 +46,7 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { assert(part); if (part->memfd >= 0) { - /* erase if requested, but ony if the memfd is not sealed yet, i.e. is writable */ + /* erase if requested, but only if the memfd is not sealed yet, i.e. is writable */ if (m->sensitive && !m->sealed) explicit_bzero_safe(part->data, part->size); @@ -451,7 +451,7 @@ int bus_message_from_header( if (!IN_SET(h->version, 1, 2)) return -EBADMSG; - if (h->type <= _SD_BUS_MESSAGE_TYPE_INVALID || h->type >= _SD_BUS_MESSAGE_TYPE_MAX) + if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) return -EBADMSG; if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) @@ -585,14 +585,14 @@ _public_ int sd_bus_message_new( sd_bus_message **m, uint8_t type) { - sd_bus_message *t; - assert_return(bus, -ENOTCONN); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state != BUS_UNSET, -ENOTCONN); assert_return(m, -EINVAL); - assert_return(type > _SD_BUS_MESSAGE_TYPE_INVALID && type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL); + /* Creation of messages with _SD_BUS_MESSAGE_TYPE_INVALID is allowed. */ + assert_return(type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL); - t = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header)); + sd_bus_message *t = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header)); if (!t) return -ENOMEM; @@ -623,6 +623,7 @@ _public_ int sd_bus_message_new_signal( int r; assert_return(bus, -ENOTCONN); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state != BUS_UNSET, -ENOTCONN); assert_return(object_path_is_valid(path), -EINVAL); assert_return(interface_name_is_valid(interface), -EINVAL); @@ -663,6 +664,7 @@ _public_ int sd_bus_message_new_method_call( int r; assert_return(bus, -ENOTCONN); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state != BUS_UNSET, -ENOTCONN); assert_return(!destination || service_name_is_valid(destination), -EINVAL); assert_return(object_path_is_valid(path), -EINVAL); @@ -3020,7 +3022,7 @@ int bus_body_part_map(struct bus_body_part *part) { return 0; } - shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); + shift = PAGE_OFFSET(part->memfd_offset); psz = PAGE_ALIGN(part->size + shift); if (part->memfd >= 0) @@ -5497,9 +5499,6 @@ int bus_message_parse_fields(sd_bus_message *m) { if (m->reply_cookie == 0 || !m->error.name) return -EBADMSG; break; - - default: - assert_not_reached("Bad message type"); } /* Refuse non-local messages that claim they are local */ @@ -5931,18 +5930,31 @@ int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) { } _public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) { + static bool warned = false; + assert_return(m, -EINVAL); assert_return(priority, -EINVAL); - *priority = m->priority; + if (!warned) { + log_debug("sd_bus_message_get_priority() is deprecated and always returns 0."); + warned = true; + } + + *priority = 0; return 0; } _public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) { + static bool warned = false; + assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - m->priority = priority; + if (!warned) { + log_debug("sd_bus_message_set_priority() is deprecated and does nothing."); + warned = true; + } + return 0; } diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index a88a531e1..5d869213a 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -76,7 +76,6 @@ struct sd_bus_message { usec_t monotonic; usec_t realtime; uint64_t seqnum; - int64_t priority; uint64_t verify_destination_id; bool sealed:1; diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 04d6adddc..6abac8822 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -56,7 +56,7 @@ static int node_vtable_get_userdata( static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) { assert(p); - if (!u) + if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET)) return SIZE_TO_PTR(p->x.method.offset); /* don't add offset on NULL, to make ubsan happy */ return (uint8_t*) u + p->x.method.offset; @@ -65,7 +65,7 @@ static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) { static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) { assert(p); - if (!u) + if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET)) return SIZE_TO_PTR(p->x.property.offset); /* as above */ return (uint8_t*) u + p->x.property.offset; @@ -780,7 +780,7 @@ static int vtable_append_all_properties( if (v->flags & SD_BUS_VTABLE_HIDDEN) continue; - /* Let's not include properties marked as "explicit" in any message that contians a generic + /* Let's not include properties marked as "explicit" in any message that contains a generic * dump of properties, but only in those generated as a response to an explicit request. */ if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) continue; @@ -827,10 +827,10 @@ static int property_get_all_callbacks_run( if (r < 0) return r; - found_interface = !iface || - streq(iface, "org.freedesktop.DBus.Properties") || - streq(iface, "org.freedesktop.DBus.Peer") || - streq(iface, "org.freedesktop.DBus.Introspectable"); + found_interface = !iface || STR_IN_SET(iface, + "org.freedesktop.DBus.Properties", + "org.freedesktop.DBus.Peer", + "org.freedesktop.DBus.Introspectable"); LIST_FOREACH(vtables, c, first) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -939,7 +939,6 @@ int introspect_path( sd_bus_error *error) { _cleanup_set_free_free_ Set *s = NULL; - const char *previous_interface = NULL; _cleanup_(introspect_free) struct introspect intro = {}; struct node_vtable *c; bool empty; @@ -984,23 +983,11 @@ int introspect_path( if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) continue; - if (!streq_ptr(previous_interface, c->interface)) { - if (previous_interface) - fputs(" \n", intro.f); - - fprintf(intro.f, " \n", c->interface); - } - - r = introspect_write_interface(&intro, c->vtable); + r = introspect_write_interface(&intro, c->interface, c->vtable); if (r < 0) return r; - - previous_interface = c->interface; } - if (previous_interface) - fputs(" \n", intro.f); - if (empty) { /* Nothing?, let's see if we exist at all, and if not * refuse to do anything */ diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h index d01fd8e6a..7e1cd3c31 100644 --- a/src/libsystemd/sd-bus/bus-protocol.h +++ b/src/libsystemd/sd-bus/bus-protocol.h @@ -103,60 +103,3 @@ enum { BUS_START_REPLY_SUCCESS = 1, BUS_START_REPLY_ALREADY_RUNNING = 2, }; - -#define BUS_INTROSPECT_DOCTYPE \ - "\n" - -#define BUS_INTROSPECT_INTERFACE_PEER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 0adfc97be..fc7e8e844 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -136,11 +136,10 @@ static int bus_socket_write_auth(sd_bus *b) { if (b->prefer_writev) k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); else { - struct msghdr mh; - zero(mh); - - mh.msg_iov = b->auth_iovec + b->auth_index; - mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index; + struct msghdr mh = { + .msg_iov = b->auth_iovec + b->auth_index, + .msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index, + }; k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); if (k < 0 && errno == ENOTSOCK) { @@ -519,10 +518,7 @@ static int bus_socket_read_auth(sd_bus *b) { ssize_t k; int r; void *p; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)) control; bool handle_cmsg = false; assert(b); @@ -551,11 +547,12 @@ static int bus_socket_read_auth(sd_bus *b) { if (b->prefer_readv) k = readv(b->input_fd, &iov, 1); else { - zero(mh); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); + mh = (struct msghdr) { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; k = recvmsg_safe(b->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (k == -ENOTSOCK) { @@ -1169,10 +1166,7 @@ int bus_socket_read_message(sd_bus *bus) { size_t need; int r; void *b; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)) control; bool handle_cmsg = false; assert(bus); @@ -1196,11 +1190,12 @@ int bus_socket_read_message(sd_bus *bus) { if (bus->prefer_readv) k = readv(bus->input_fd, &iov, 1); else { - zero(mh); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); + mh = (struct msghdr) { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; k = recvmsg_safe(bus->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (k == -ENOTSOCK) { @@ -1267,21 +1262,15 @@ int bus_socket_read_message(sd_bus *bus) { } int bus_socket_process_opening(sd_bus *b) { - int error = 0; + int error = 0, events, r; socklen_t slen = sizeof(error); - struct pollfd p = { - .fd = b->output_fd, - .events = POLLOUT, - }; - int r; assert(b->state == BUS_OPENING); - r = poll(&p, 1, 0); - if (r < 0) - return -errno; - - if (!(p.revents & (POLLOUT|POLLERR|POLLHUP))) + events = fd_wait_for_event(b->output_fd, POLLOUT, 0); + if (events < 0) + return events; + if (!(events & (POLLOUT|POLLERR|POLLHUP))) return 0; r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen); @@ -1289,7 +1278,7 @@ int bus_socket_process_opening(sd_bus *b) { b->last_connect_error = errno; else if (error != 0) b->last_connect_error = error; - else if (p.revents & (POLLERR|POLLHUP)) + else if (events & (POLLERR|POLLHUP)) b->last_connect_error = ECONNREFUSED; else return bus_socket_start_auth(b); diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index f60985492..13fd52ffd 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -6,6 +6,7 @@ #include "bus-internal.h" #include "bus-track.h" #include "bus-util.h" +#include "string-util.h" struct track_item { unsigned n_ref; diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c index 18564a538..585f8424b 100644 --- a/src/libsystemd/sd-bus/bus-type.c +++ b/src/libsystemd/sd-bus/bus-type.c @@ -4,6 +4,7 @@ #include "sd-bus.h" +#include "bus-internal.h" #include "bus-type.h" bool bus_type_is_valid(char c) { @@ -135,3 +136,27 @@ int bus_type_get_size(char c) { return -EINVAL; } + +_public_ int sd_bus_interface_name_is_valid(const char *p) { + assert_return(p, -EINVAL); + + return interface_name_is_valid(p); +} + +_public_ int sd_bus_service_name_is_valid(const char *p) { + assert_return(p, -EINVAL); + + return service_name_is_valid(p); +} + +_public_ int sd_bus_member_name_is_valid(const char *p) { + assert_return(p, -EINVAL); + + return member_name_is_valid(p); +} + +_public_ int sd_bus_object_path_is_valid(const char *p) { + assert_return(p, -EINVAL); + + return object_path_is_valid(p); +} diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 7ad03680f..9de5e454a 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -284,7 +284,7 @@ _public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) { return 0; } -_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) { +_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const *argv) { _cleanup_strv_free_ char **a = NULL; int r; @@ -504,7 +504,6 @@ static int synthesize_connected_signal(sd_bus *bus) { } void bus_set_state(sd_bus *bus, enum bus_state state) { - static const char * const table[_BUS_STATE_MAX] = { [BUS_UNSET] = "UNSET", [BUS_WATCH_BIND] = "WATCH_BIND", @@ -980,9 +979,8 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) return -EINVAL; free_and_replace(b->machine, machine); - } else { + } else b->machine = mfree(b->machine); - } if (pid) { r = parse_pid(pid, &b->nspid); @@ -1268,13 +1266,16 @@ _public_ int sd_bus_open(sd_bus **ret) { int bus_set_address_system(sd_bus *b) { const char *e; + int r; + assert(b); e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); - return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); + r = sd_bus_set_address(b, e ?: DEFAULT_SYSTEM_BUS_ADDRESS); + if (r >= 0) + b->is_system = true; + return r; } _public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *description) { @@ -1298,7 +1299,6 @@ _public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *descr return r; b->bus_client = true; - b->is_system = true; /* Let's do per-method access control on the system bus. We * need the caller's UID and capability set for that. */ @@ -1319,29 +1319,34 @@ _public_ int sd_bus_open_system(sd_bus **ret) { } int bus_set_address_user(sd_bus *b) { - const char *e; - _cleanup_free_ char *ee = NULL, *s = NULL; + const char *a; + _cleanup_free_ char *_a = NULL; + int r; assert(b); - e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); + a = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + if (!a) { + const char *e; + _cleanup_free_ char *ee = NULL; - e = secure_getenv("XDG_RUNTIME_DIR"); - if (!e) - return -ENOENT; + e = secure_getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENOENT; - ee = bus_address_escape(e); - if (!ee) - return -ENOMEM; + ee = bus_address_escape(e); + if (!ee) + return -ENOMEM; - if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0) - return -ENOMEM; + if (asprintf(&_a, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0) + return -ENOMEM; + a = _a; + } - b->address = TAKE_PTR(s); - - return 0; + r = sd_bus_set_address(b, a); + if (r >= 0) + b->is_user = true; + return r; } _public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *description) { @@ -1365,7 +1370,6 @@ _public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *descrip return r; b->bus_client = true; - b->is_user = true; /* We don't do any per-method access control on the user bus. */ b->trusted = true; @@ -1837,7 +1841,7 @@ static int dispatch_wqueue(sd_bus *bus) { return ret; } -static int bus_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { +static int bus_read_message(sd_bus *bus) { assert(bus); return bus_socket_read_message(bus); @@ -1864,17 +1868,13 @@ static void rqueue_drop_one(sd_bus *bus, size_t i) { bus->rqueue_size--; } -static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **m) { +static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) { int r, ret = 0; assert(bus); assert(m); assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)); - /* Note that the priority logic is only available on kdbus, - * where the rqueue is unused. We check the rqueue here - * anyway, because it's simple... */ - for (;;) { if (bus->rqueue_size > 0) { /* Dispatch a queued message */ @@ -1884,7 +1884,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd } /* Try to read a new message */ - r = bus_read_message(bus, hint_priority, priority); + r = bus_read_message(bus); if (r < 0) return r; if (r == 0) { @@ -1902,9 +1902,10 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { assert_return(m, -EINVAL); - if (!bus) - bus = m->bus; - + if (bus) + assert_return(bus = bus_resolve(bus), -ENOPKG); + else + assert_return(bus = m->bus, -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -1986,9 +1987,10 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat assert_return(m, -EINVAL); - if (!bus) - bus = m->bus; - + if (bus) + assert_return(bus = bus_resolve(bus), -ENOPKG); + else + assert_return(bus = m->bus, -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -2051,9 +2053,10 @@ _public_ int sd_bus_call_async( assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL); - if (!bus) - bus = m->bus; - + if (bus) + assert_return(bus = bus_resolve(bus), -ENOPKG); + else + assert_return(bus = m->bus, -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -2157,9 +2160,10 @@ _public_ int sd_bus_call( bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error); bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error); - if (!bus) - bus = m->bus; - + if (bus) + assert_return(bus = bus_resolve(bus), -ENOPKG); + else + assert_return(bus = m->bus, -ENOTCONN); bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); if (!BUS_IS_OPEN(bus->state)) { @@ -2237,7 +2241,7 @@ _public_ int sd_bus_call( i++; } - r = bus_read_message(bus, false, 0); + r = bus_read_message(bus); if (r < 0) { if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); @@ -2286,7 +2290,6 @@ fail: } _public_ int sd_bus_get_fd(sd_bus *bus) { - assert_return(bus, -EINVAL); assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->input_fd == bus->output_fd, -EPERM); @@ -2777,7 +2780,7 @@ static int dispatch_track(sd_bus *bus) { return 1; } -static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { +static int process_running(sd_bus *bus, sd_bus_message **ret) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int r; @@ -2796,7 +2799,7 @@ static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd if (r != 0) goto null_message; - r = dispatch_rqueue(bus, hint_priority, priority, &m); + r = dispatch_rqueue(bus, &m); if (r < 0) return r; if (!m) @@ -2982,7 +2985,7 @@ finish: return r; } -static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { +static int bus_process_internal(sd_bus *bus, sd_bus_message **ret) { int r; /* Returns 0 when we didn't do anything. This should cause the @@ -3022,7 +3025,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_RUNNING: case BUS_HELLO: - r = process_running(bus, hint_priority, priority, ret); + r = process_running(bus, ret); if (r >= 0) return r; @@ -3049,11 +3052,11 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit } _public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { - return bus_process_internal(bus, false, 0, ret); + return bus_process_internal(bus, ret); } _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) { - return bus_process_internal(bus, true, priority, ret); + return bus_process_internal(bus, ret); } static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { @@ -3118,8 +3121,15 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL); if (r < 0) return -errno; + if (r == 0) + return 0; - return r > 0 ? 1 : 0; + if (p[0].revents & POLLNVAL) + return -EBADF; + if (n >= 2 && (p[1].revents & POLLNVAL)) + return -EBADF; + + return 1; } _public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { @@ -3677,31 +3687,31 @@ _public_ int sd_bus_detach_event(sd_bus *bus) { } _public_ sd_event* sd_bus_get_event(sd_bus *bus) { - assert_return(bus, NULL); + assert_return(bus = bus_resolve(bus), NULL); return bus->event; } _public_ sd_bus_message* sd_bus_get_current_message(sd_bus *bus) { - assert_return(bus, NULL); + assert_return(bus = bus_resolve(bus), NULL); return bus->current_message; } _public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) { - assert_return(bus, NULL); + assert_return(bus = bus_resolve(bus), NULL); return bus->current_slot; } _public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) { - assert_return(bus, NULL); + assert_return(bus = bus_resolve(bus), NULL); return bus->current_handler; } _public_ void* sd_bus_get_current_userdata(sd_bus *bus) { - assert_return(bus, NULL); + assert_return(bus = bus_resolve(bus), NULL); return bus->current_userdata; } @@ -4018,7 +4028,6 @@ _public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { } _public_ int sd_bus_get_address(sd_bus *bus, const char **address) { - assert_return(bus, -EINVAL); assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(address, -EINVAL); diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c index 9c8d1434b..cbc315892 100644 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ b/src/libsystemd/sd-bus/test-bus-introspect.c @@ -14,11 +14,11 @@ static void test_manual_introspection(const sd_bus_vtable vtable[]) { assert_se(introspect_begin(&intro, false) >= 0); - fprintf(intro.f, " \n"); - assert_se(introspect_write_interface(&intro, vtable) >= 0); - fputs(" \n", intro.f); - + assert_se(introspect_write_interface(&intro, "org.foo", vtable) >= 0); + /* write again to check if output looks OK for a different interface */ + assert_se(introspect_write_interface(&intro, "org.foo.bar", vtable) >= 0); assert_se(introspect_finish(&intro, &s) == 0); + fputs(s, stdout); fputs("\n", stdout); } diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 4cd71cb2d..bbe7cd76d 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include "process-util.h" #include "socket-util.h" #include "strv.h" +#include "time-util.h" #include "util.h" #define SNDBUF_SIZE (8*1024*1024) @@ -551,6 +553,28 @@ finish: return r; } +_public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) { + _cleanup_close_pair_ int pipe_fd[2] = { -1, -1 }; + int r; + + if (pipe2(pipe_fd, O_CLOEXEC) < 0) + return -errno; + + r = sd_pid_notify_with_fds(0, unset_environment, "BARRIER=1", &pipe_fd[1], 1); + if (r <= 0) + return r; + + pipe_fd[1] = safe_close(pipe_fd[1]); + + r = fd_wait_for_event(pipe_fd[0], 0 /* POLLHUP is implicit */, timeout); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; + + return 1; +} + _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); } diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index a1932f41f..95dfc2f07 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -71,14 +71,14 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer sd_device_unref(enumerator->devices[i]); free(enumerator->devices); - set_free_free(enumerator->match_subsystem); - set_free_free(enumerator->nomatch_subsystem); - hashmap_free_free_free(enumerator->match_sysattr); - hashmap_free_free_free(enumerator->nomatch_sysattr); - hashmap_free_free_free(enumerator->match_property); - set_free_free(enumerator->match_sysname); - set_free_free(enumerator->match_tag); - set_free_free(enumerator->match_parent); + set_free(enumerator->match_subsystem); + set_free(enumerator->nomatch_subsystem); + hashmap_free(enumerator->match_sysattr); + hashmap_free(enumerator->nomatch_sysattr); + hashmap_free(enumerator->match_property); + set_free(enumerator->match_sysname); + set_free(enumerator->match_tag); + set_free(enumerator->match_parent); return mfree(enumerator); } @@ -97,89 +97,49 @@ _public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enum else set = &enumerator->nomatch_subsystem; - r = set_ensure_allocated(set, NULL); - if (r < 0) - return r; - - r = set_put_strdup(*set, subsystem); - if (r < 0) + r = set_put_strdup(set, subsystem); + if (r <= 0) return r; enumerator->scan_uptodate = false; - return 0; + return 1; } -_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { - _cleanup_free_ char *sysattr = NULL, *value = NULL; +_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match) { Hashmap **hashmap; int r; assert_return(enumerator, -EINVAL); - assert_return(_sysattr, -EINVAL); + assert_return(sysattr, -EINVAL); if (match) hashmap = &enumerator->match_sysattr; else hashmap = &enumerator->nomatch_sysattr; - r = hashmap_ensure_allocated(hashmap, NULL); - if (r < 0) + r = hashmap_put_strdup(hashmap, sysattr, value); + if (r <= 0) return r; - sysattr = strdup(_sysattr); - if (!sysattr) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(*hashmap, sysattr, value); - if (r < 0) - return r; - - sysattr = NULL; - value = NULL; - enumerator->scan_uptodate = false; - return 0; + return 1; } -_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { - _cleanup_free_ char *property = NULL, *value = NULL; +_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value) { int r; assert_return(enumerator, -EINVAL); - assert_return(_property, -EINVAL); + assert_return(property, -EINVAL); - r = hashmap_ensure_allocated(&enumerator->match_property, NULL); - if (r < 0) + r = hashmap_put_strdup(&enumerator->match_property, property, value); + if (r <= 0) return r; - property = strdup(_property); - if (!property) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(enumerator->match_property, property, value); - if (r < 0) - return r; - - property = NULL; - value = NULL; - enumerator->scan_uptodate = false; - return 0; + return 1; } _public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { @@ -188,17 +148,13 @@ _public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumer assert_return(enumerator, -EINVAL); assert_return(sysname, -EINVAL); - r = set_ensure_allocated(&enumerator->match_sysname, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_sysname, sysname); - if (r < 0) + r = set_put_strdup(&enumerator->match_sysname, sysname); + if (r <= 0) return r; enumerator->scan_uptodate = false; - return 0; + return 1; } _public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { @@ -207,52 +163,41 @@ _public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator assert_return(enumerator, -EINVAL); assert_return(tag, -EINVAL); - r = set_ensure_allocated(&enumerator->match_tag, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_tag, tag); - if (r < 0) + r = set_put_strdup(&enumerator->match_tag, tag); + if (r <= 0) return r; enumerator->scan_uptodate = false; - return 0; -} - -static void device_enumerator_clear_match_parent(sd_device_enumerator *enumerator) { - if (!enumerator) - return; - - set_clear_free(enumerator->match_parent); + return 1; } int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) { const char *path; int r; - assert_return(enumerator, -EINVAL); - assert_return(parent, -EINVAL); + assert(enumerator); + assert(parent); r = sd_device_get_syspath(parent, &path); if (r < 0) return r; - r = set_ensure_allocated(&enumerator->match_parent, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_parent, path); - if (r < 0) + r = set_put_strdup(&enumerator->match_parent, path); + if (r <= 0) return r; enumerator->scan_uptodate = false; - return 0; + return 1; } _public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { - device_enumerator_clear_match_parent(enumerator); + assert_return(enumerator, -EINVAL); + assert_return(parent, -EINVAL); + + set_clear(enumerator->match_parent); + return device_enumerator_add_match_parent_incremental(enumerator, parent); } @@ -263,7 +208,7 @@ _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enum enumerator->scan_uptodate = false; - return 0; + return 1; } int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { @@ -273,7 +218,7 @@ int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) enumerator->scan_uptodate = false; - return 0; + return 1; } static int device_compare(sd_device * const *_a, sd_device * const *_b) { diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 023fe0fcd..1fe5c1a6b 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -94,8 +94,8 @@ 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); int device_set_devmode(sd_device *device, const char *devmode); -int device_set_devname(sd_device *device, const char *_devname); -int device_set_devtype(sd_device *device, const char *_devtype); +int device_set_devname(sd_device *device, const char *devname); +int device_set_devtype(sd_device *device, const char *devtype); int device_set_devnum(sd_device *device, const char *major, const char *minor); int device_set_subsystem(sd_device *device, const char *_subsystem); int device_set_driver(sd_device *device, const char *_driver); diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index 42753abe0..bed45da8e 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "sd-device.h" @@ -13,10 +15,12 @@ #include "device-monitor-private.h" #include "device-private.h" #include "device-util.h" +#include "errno-util.h" #include "fd-util.h" #include "format-util.h" #include "hashmap.h" #include "io-util.h" +#include "missing_socket.h" #include "mountpoint-util.h" #include "set.h" #include "socket-util.h" @@ -163,12 +167,54 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, if (fd >= 0) { r = monitor_set_nl_address(m); - if (r < 0) - return log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m"); + if (r < 0) { + log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m"); + goto fail; + } + } + + if (DEBUG_LOGGING) { + _cleanup_close_ int netns = -1; + + /* So here's the thing: only AF_NETLINK sockets from the main network namespace will get + * hardware events. Let's check if ours is from there, and if not generate a debug message, + * since we cannot possibly work correctly otherwise. This is just a safety check to make + * things easier to debug. */ + + netns = ioctl(m->sock, SIOCGSKNS); + if (netns < 0) + log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns: %m"); + else { + struct stat a, b; + + if (fstat(netns, &a) < 0) { + r = log_debug_errno(errno, "sd-device-monitor: Failed to stat netns of udev netlink socket: %m"); + goto fail; + } + + if (stat("/proc/1/ns/net", &b) < 0) { + if (ERRNO_IS_PRIVILEGE(errno)) + /* If we can't access PID1's netns info due to permissions, it's fine, this is a + * safety check only after all. */ + log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns: %m"); + else + log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns: %m"); + + } else if (a.st_dev != b.st_dev || a.st_ino != b.st_ino) + log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events."); + } } *ret = TAKE_PTR(m); return 0; + +fail: + /* Let's unset the socket fd in the monitor object before we destroy it so that the fd passed in is + * not closed on failure. */ + if (fd >= 0) + m->sock = -1; + + return r; } _public_ int sd_device_monitor_new(sd_device_monitor **ret) { @@ -361,13 +407,13 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { .iov_base = &buf, .iov_len = sizeof(buf) }; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; union sockaddr_union snl; struct msghdr smsg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cred_msg, - .msg_controllen = sizeof(cred_msg), + .msg_control = &control, + .msg_controllen = sizeof(control), .msg_name = &snl, .msg_namelen = sizeof(snl), }; @@ -512,10 +558,9 @@ int device_monitor_send_device( r = device_get_properties_nulstr(device, (const uint8_t **) &buf, &blen); if (r < 0) return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m"); - if (blen < 32) { - log_device_debug(device, "sd-device-monitor: Length of device property nulstr is too small to contain valid device information"); - return -EINVAL; - } + if (blen < 32) + log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device-monitor: Length of device property nulstr is too small to contain valid device information"); /* fill in versioned header */ r = sd_device_get_subsystem(device, &val); @@ -713,30 +758,13 @@ _public_ int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_moni } _public_ int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag) { - _cleanup_free_ char *t = NULL; - int r; - assert_return(m, -EINVAL); assert_return(tag, -EINVAL); - t = strdup(tag); - if (!t) - return -ENOMEM; - - r = set_ensure_allocated(&m->tag_filter, &string_hash_ops); - if (r < 0) - return r; - - r = set_put(m->tag_filter, t); - if (r == -EEXIST) - return 0; - if (r < 0) - return r; - - TAKE_PTR(t); - m->filter_uptodate = false; - - return 0; + int r = set_put_strdup(&m->tag_filter, tag); + if (r > 0) + m->filter_uptodate = false; + return r; } _public_ int sd_device_monitor_filter_remove(sd_device_monitor *m) { diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 16f8627bd..1e61732df 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -363,10 +363,9 @@ static int device_append(sd_device *device, char *key, const char **_major, cons assert(_minor); value = strchr(key, '='); - if (!value) { - log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key); - return -EINVAL; - } + if (!value) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Not a key-value pair: '%s'", key); *value = '\0'; @@ -400,10 +399,9 @@ void device_seal(sd_device *device) { static int device_verify(sd_device *device) { assert(device); - if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0) { - log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); - return -EINVAL; - } + if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); device->sealed = true; @@ -464,10 +462,10 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { key = (char*)&nulstr[i]; end = memchr(key, '\0', len - i); - if (!end) { - log_device_debug(device, "sd-device: Failed to parse nulstr"); - return -EINVAL; - } + if (!end) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Failed to parse nulstr"); + i += end - key + 1; r = device_append(device, key, &major, &minor); diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h index a25682d89..1a1795d97 100644 --- a/src/libsystemd/sd-device/device-util.h +++ b/src/libsystemd/sd-device/device-util.h @@ -37,7 +37,7 @@ sd_device *_d = (device); \ int _level = (level), _error = (error); \ \ - if (_d && _unlikely_(log_get_max_level() >= _level)) \ + if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \ (void) sd_device_get_sysname(_d, &_sysname); \ log_object_internal(_level, _error, PROJECT_FILE, __LINE__, __func__, \ _sysname ? "DEVICE=" : NULL, _sysname, \ diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 1f2451f8e..3bba17aff 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -68,9 +68,9 @@ static sd_device *device_free(sd_device *device) { ordered_hashmap_free_free_free(device->properties); ordered_hashmap_free_free_free(device->properties_db); hashmap_free_free_free(device->sysattr_values); - set_free_free(device->sysattrs); - set_free_free(device->tags); - set_free_free(device->devlinks); + set_free(device->sysattrs); + set_free(device->tags); + set_free(device->devlinks); return mfree(device); } @@ -320,24 +320,22 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s return -ENODEV; } -int device_set_devtype(sd_device *device, const char *_devtype) { - _cleanup_free_ char *devtype = NULL; +int device_set_devtype(sd_device *device, const char *devtype) { + _cleanup_free_ char *t = NULL; int r; assert(device); - assert(_devtype); + assert(devtype); - devtype = strdup(_devtype); - if (!devtype) + t = strdup(devtype); + if (!t) return -ENOMEM; - r = device_add_property_internal(device, "DEVTYPE", devtype); + r = device_add_property_internal(device, "DEVTYPE", t); if (r < 0) return r; - free_and_replace(device->devtype, devtype); - - return 0; + return free_and_replace(device->devtype, t); } int device_set_ifindex(sd_device *device, const char *name) { @@ -359,30 +357,25 @@ int device_set_ifindex(sd_device *device, const char *name) { return 0; } -int device_set_devname(sd_device *device, const char *_devname) { - _cleanup_free_ char *devname = NULL; +int device_set_devname(sd_device *device, const char *devname) { + _cleanup_free_ char *t = NULL; int r; assert(device); - assert(_devname); + assert(devname); - if (_devname[0] != '/') { - r = asprintf(&devname, "/dev/%s", _devname); - if (r < 0) - return -ENOMEM; - } else { - devname = strdup(_devname); - if (!devname) - return -ENOMEM; - } + if (devname[0] != '/') + t = strjoin("/dev/", devname); + else + t = strdup(devname); + if (!t) + return -ENOMEM; - r = device_add_property_internal(device, "DEVNAME", devname); + r = device_add_property_internal(device, "DEVNAME", t); if (r < 0) return r; - free_and_replace(device->devname, devname); - - return 0; + return free_and_replace(device->devname, t); } int device_set_devmode(sd_device *device, const char *_devmode) { @@ -1078,11 +1071,7 @@ int device_add_tag(sd_device *device, const char *tag) { if (!is_valid_tag(tag)) return -EINVAL; - r = set_ensure_allocated(&device->tags, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->tags, tag); + r = set_put_strdup(&device->tags, tag); if (r < 0) return r; @@ -1098,11 +1087,7 @@ int device_add_devlink(sd_device *device, const char *devlink) { assert(device); assert(devlink); - r = set_ensure_allocated(&device->devlinks, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->devlinks, devlink); + r = set_put_strdup(&device->devlinks, devlink); if (r < 0) return r; @@ -1258,17 +1243,15 @@ int device_get_id_filename(sd_device *device, const char **ret) { if (!subsystem) return -EINVAL; - if (streq(subsystem, "drivers")) { + + if (streq(subsystem, "drivers")) /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem * encoded as well */ - r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname); - if (r < 0) - return -ENOMEM; - } else { - r = asprintf(&id, "+%s:%s", subsystem, sysname); - if (r < 0) - return -ENOMEM; - } + id = strjoin("+drivers:", device->driver_subsystem, ":", sysname); + else + id = strjoin("+", subsystem, ":", sysname); + if (!id) + return -ENOMEM; } device->id_filename = TAKE_PTR(id); @@ -1572,38 +1555,68 @@ _public_ const char *sd_device_get_property_next(sd_device *device, const char * return key; } -static int device_sysattrs_read_all(sd_device *device) { +static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir) { + _cleanup_free_ char *path_dir = NULL; _cleanup_closedir_ DIR *dir = NULL; - const char *syspath; struct dirent *dent; + const char *syspath; int r; - assert(device); - - if (device->sysattrs_read) - return 0; - r = sd_device_get_syspath(device, &syspath); if (r < 0) return r; - dir = opendir(syspath); + if (subdir) { + _cleanup_free_ char *p = NULL; + + p = path_join(syspath, subdir, "uevent"); + if (!p) + return -ENOMEM; + + if (access(p, F_OK) >= 0) + /* this is a child device, skipping */ + return 0; + if (errno != ENOENT) { + log_device_debug_errno(device, errno, "sd-device: Failed to stat %s, ignoring subdir: %m", p); + return 0; + } + + path_dir = path_join(syspath, subdir); + if (!path_dir) + return -ENOMEM; + } + + dir = opendir(path_dir ?: syspath); if (!dir) return -errno; - r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); - if (r < 0) - return r; - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *p = NULL; struct stat statbuf; - /* only handle symlinks and regular files */ - if (!IN_SET(dent->d_type, DT_LNK, DT_REG)) + if (dot_or_dot_dot(dent->d_name)) continue; - path = path_join(syspath, dent->d_name); + /* only handle symlinks, regular files, and directories */ + if (!IN_SET(dent->d_type, DT_LNK, DT_REG, DT_DIR)) + continue; + + if (subdir) { + p = path_join(subdir, dent->d_name); + if (!p) + return -ENOMEM; + } + + if (dent->d_type == DT_DIR) { + /* read subdirectory */ + r = device_sysattrs_read_all_internal(device, p ?: dent->d_name); + if (r < 0) + return r; + + continue; + } + + path = path_join(syspath, p ?: dent->d_name); if (!path) return -ENOMEM; @@ -1613,11 +1626,26 @@ static int device_sysattrs_read_all(sd_device *device) { if (!(statbuf.st_mode & S_IRUSR)) continue; - r = set_put_strdup(device->sysattrs, dent->d_name); + r = set_put_strdup(&device->sysattrs, p ?: dent->d_name); if (r < 0) return r; } + return 0; +} + +static int device_sysattrs_read_all(sd_device *device) { + int r; + + assert(device); + + if (device->sysattrs_read) + return 0; + + r = device_sysattrs_read_all_internal(device, NULL); + if (r < 0) + return r; + device->sysattrs_read = true; return 0; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index fb9db4710..860eb048f 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -1450,10 +1450,6 @@ _public_ int sd_event_add_post( assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); - r = set_ensure_allocated(&e->post_sources, NULL); - if (r < 0) - return r; - s = source_new(e, !ret, SOURCE_POST); if (!s) return -ENOMEM; @@ -1462,9 +1458,10 @@ _public_ int sd_event_add_post( s->userdata = userdata; s->enabled = SD_EVENT_ON; - r = set_put(e->post_sources, s); + r = set_ensure_put(&e->post_sources, NULL, s); if (r < 0) return r; + assert(r > 0); if (ret) *ret = s; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 3e96c98cd..746c895b6 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -746,7 +746,7 @@ static int seat_get_can(const char *seat, const char *variable) { } _public_ int sd_seat_can_multi_session(const char *seat) { - return seat_get_can(seat, "CAN_MULTI_SESSION"); + return true; } _public_ int sd_seat_can_tty(const char *seat) { diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 49ed24727..c0c77e047 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -142,8 +142,11 @@ static void test_login(void) { log_info("sd_session_get_seat(\"%s\") → \"%s\"", session, seat); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" r = sd_seat_can_multi_session(seat); - assert_se(r >= 0); +#pragma GCC diagnostic pop + assert_se(r == 1); log_info("sd_session_can_multi_seat(\"%s\") → %s", seat, yes_no(r)); r = sd_seat_can_tty(seat); diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 2648b338f..d6bf31efc 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -342,6 +342,74 @@ int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, ui return 0; } +int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(int8_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(int16_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(int32_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(int64_t)); + if (r < 0) + return r; + + return 0; +} + int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { int r; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 9401c43d0..bcd82fe16 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -238,34 +238,29 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) { return k; } -static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { +static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) { union sockaddr_union sender; - uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control; struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1, .msg_name = &sender, .msg_namelen = sizeof(sender), - .msg_control = cmsg_buffer, - .msg_controllen = sizeof(cmsg_buffer), + .msg_control = &control, + .msg_controllen = sizeof(control), }; - struct cmsghdr *cmsg; - uint32_t group = 0; ssize_t n; assert(fd >= 0); assert(iov); - n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); - if (n < 0) { - /* no data */ - if (errno == ENOBUFS) - log_debug("rtnl: kernel receive buffer overrun"); - else if (errno == EAGAIN) - log_debug("rtnl: no data in socket"); - - return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno; - } + n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); + if (n == -ENOBUFS) + return log_debug_errno(n, "rtnl: kernel receive buffer overrun"); + if (IN_SET(n, -EAGAIN, -EINTR)) + return 0; + if (n < 0) + return (int) n; if (sender.nl.nl_pid != 0) { /* not from the kernel, ignore */ @@ -273,28 +268,24 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool if (peek) { /* drop the message */ - n = recvmsg(fd, &msg, 0); + n = recvmsg_safe(fd, &msg, 0); if (n < 0) - return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno; + return (int) n; } return 0; } - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_NETLINK && - cmsg->cmsg_type == NETLINK_PKTINFO && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { - struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); + if (ret_mcast_group) { + struct nl_pktinfo *pi; - /* multi-cast group */ - group = pktinfo->group; - } + pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo); + if (pi) + *ret_mcast_group = pi->group; + else + *ret_mcast_group = 0; } - if (_group) - *_group = group; - return (int) n; } diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index e35127a4c..060458a53 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -93,9 +93,20 @@ static const NLType rtnl_link_info_data_ipvlan_types[] = { [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, }; +static const NLType rtnl_macvlan_macaddr_types[] = { + [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR }, +}; + +static const NLTypeSystem rtnl_macvlan_macaddr_type_system = { + .count = ELEMENTSOF(rtnl_macvlan_macaddr_types), + .types = rtnl_macvlan_macaddr_types, +}; + static const NLType rtnl_link_info_data_macvlan_types[] = { [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_MACVLAN_MACADDR_MODE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_MACVLAN_MACADDR_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system }, }; static const NLType rtnl_link_info_data_bridge_types[] = { @@ -316,6 +327,7 @@ static const NLType rtnl_link_info_data_can_types[] = { [IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) }, [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 }, [IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) }, + [IFLA_CAN_TERMINATION] = { .type = NETLINK_TYPE_U16 }, }; static const NLType rtnl_link_info_data_macsec_types[] = { @@ -536,15 +548,50 @@ static const NLTypeSystem rtnl_prop_list_type_system = { .types = rtnl_prop_list_types, }; +static const NLType rtnl_vf_vlan_list_types[] = { + [IFLA_VF_VLAN_INFO] = { .size = sizeof(struct ifla_vf_vlan_info) }, +}; + +static const NLTypeSystem rtnl_vf_vlan_type_system = { + .count = ELEMENTSOF(rtnl_vf_vlan_list_types), + .types = rtnl_vf_vlan_list_types, +}; + +static const NLType rtnl_vf_vlan_info_types[] = { + [IFLA_VF_MAC] = { .size = sizeof(struct ifla_vf_mac) }, + [IFLA_VF_VLAN] = { .size = sizeof(struct ifla_vf_vlan) }, + [IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system}, + [IFLA_VF_TX_RATE] = { .size = sizeof(struct ifla_vf_tx_rate) }, + [IFLA_VF_SPOOFCHK] = { .size = sizeof(struct ifla_vf_spoofchk) }, + [IFLA_VF_RATE] = { .size = sizeof(struct ifla_vf_rate) }, + [IFLA_VF_LINK_STATE] = { .size = sizeof(struct ifla_vf_link_state) }, + [IFLA_VF_RSS_QUERY_EN] = { .size = sizeof(struct ifla_vf_rss_query_en) }, + [IFLA_VF_TRUST] = { .size = sizeof(struct ifla_vf_trust) }, + [IFLA_VF_IB_NODE_GUID] = { .size = sizeof(struct ifla_vf_guid) }, + [IFLA_VF_IB_PORT_GUID] = { .size = sizeof(struct ifla_vf_guid) }, +}; + +static const NLTypeSystem rtnl_vf_vlan_info_type_system = { + .count = ELEMENTSOF(rtnl_vf_vlan_info_types), + .types = rtnl_vf_vlan_info_types, +}; + +static const NLType rtnl_link_io_srv_types[] = { + [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system }, +}; + +static const NLTypeSystem rtnl_io_srv_type_system = { + .count = ELEMENTSOF(rtnl_link_io_srv_types), + .types = rtnl_link_io_srv_types, +}; + static const NLType rtnl_link_types[] = { [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, [IFLA_MTU] = { .type = NETLINK_TYPE_U32 }, [IFLA_LINK] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_QDISC], -*/ + [IFLA_QDISC] = { .type = NETLINK_TYPE_STRING }, [IFLA_STATS] = { .size = sizeof(struct rtnl_link_stats) }, /* [IFLA_COST], @@ -565,10 +612,8 @@ static const NLType rtnl_link_types[] = { [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system }, [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 }, [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 }, -/* - [IFLA_NUM_VF], - [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, }, -*/ + [IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system }, [IFLA_STATS64] = { .size = sizeof(struct rtnl_link_stats64) }, /* [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED }, @@ -745,6 +790,12 @@ static const NLTypeSystem rtnl_nexthop_type_system = { .types = rtnl_nexthop_types, }; +static const NLType rtnl_tca_option_data_cake_types[] = { + [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 }, + [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 }, + [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 }, +}; + static const NLType rtnl_tca_option_data_codel_types[] = { [TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 }, [TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 }, @@ -753,6 +804,36 @@ static const NLType rtnl_tca_option_data_codel_types[] = { [TCA_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 }, }; +static const NLType rtnl_tca_option_data_drr_types[] = { + [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLType rtnl_tca_option_data_ets_quanta_types[] = { + [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, }, +}; + +static const NLTypeSystem rtnl_tca_option_data_ets_quanta_type_system = { + .count = ELEMENTSOF(rtnl_tca_option_data_ets_quanta_types), + .types = rtnl_tca_option_data_ets_quanta_types, +}; + +static const NLType rtnl_tca_option_data_ets_prio_types[] = { + [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, }, +}; + +static const NLTypeSystem rtnl_tca_option_data_ets_prio_type_system = { + .count = ELEMENTSOF(rtnl_tca_option_data_ets_prio_types), + .types = rtnl_tca_option_data_ets_prio_types, +}; + +static const NLType rtnl_tca_option_data_ets_types[] = { + [TCA_ETS_NBANDS] = { .type = NETLINK_TYPE_U8 }, + [TCA_ETS_NSTRICT] = { .type = NETLINK_TYPE_U8 }, + [TCA_ETS_QUANTA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system }, + [TCA_ETS_PRIOMAP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system }, + [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 }, +}; + static const NLType rtnl_tca_option_data_fq_types[] = { [TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 }, [TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 }, @@ -780,6 +861,36 @@ static const NLType rtnl_tca_option_data_fq_codel_types[] = { [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 }, }; +static const NLType rtnl_tca_option_data_gred_types[] = { + [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) }, +}; + +static const NLType rtnl_tca_option_data_hhf_types[] = { + [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLType rtnl_tca_option_data_htb_types[] = { + [TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) }, + [TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) }, + [TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE }, + [TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE }, + [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 }, + [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 }, +}; + +static const NLType rtnl_tca_option_data_pie_types[] = { + [TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLType rtnl_tca_option_data_qfq_types[] = { + [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 }, + [TCA_QFQ_LMAX] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLType rtnl_tca_option_data_sfb_types[] = { + [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) }, +}; + static const NLType rtnl_tca_option_data_tbf_types[] = { [TCA_TBF_PARMS] = { .size = sizeof(struct tc_tbf_qopt) }, [TCA_TBF_RTAB] = { .size = TC_RTAB_SIZE }, @@ -791,21 +902,48 @@ static const NLType rtnl_tca_option_data_tbf_types[] = { }; static const char* const nl_union_tca_option_data_table[] = { + [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake", [NL_UNION_TCA_OPTION_DATA_CODEL] = "codel", + [NL_UNION_TCA_OPTION_DATA_DRR] = "drr", + [NL_UNION_TCA_OPTION_DATA_ETS] = "ets", [NL_UNION_TCA_OPTION_DATA_FQ] = "fq", [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel", + [NL_UNION_TCA_OPTION_DATA_GRED] = "gred", + [NL_UNION_TCA_OPTION_DATA_HHF] = "hhf", + [NL_UNION_TCA_OPTION_DATA_HTB] = "htb", + [NL_UNION_TCA_OPTION_DATA_PIE] = "pie", + [NL_UNION_TCA_OPTION_DATA_QFQ] = "qfq", + [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb", [NL_UNION_TCA_OPTION_DATA_TBF] = "tbf", }; DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData); static const NLTypeSystem rtnl_tca_option_data_type_systems[] = { + [NL_UNION_TCA_OPTION_DATA_CAKE] = { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types), + .types = rtnl_tca_option_data_cake_types }, [NL_UNION_TCA_OPTION_DATA_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types), .types = rtnl_tca_option_data_codel_types }, + [NL_UNION_TCA_OPTION_DATA_DRR] = { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types), + .types = rtnl_tca_option_data_drr_types }, + [NL_UNION_TCA_OPTION_DATA_ETS] = { .count = ELEMENTSOF(rtnl_tca_option_data_ets_types), + .types = rtnl_tca_option_data_ets_types }, [NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types), .types = rtnl_tca_option_data_fq_types }, [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types), .types = rtnl_tca_option_data_fq_codel_types }, + [NL_UNION_TCA_OPTION_DATA_GRED] = { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types), + .types = rtnl_tca_option_data_gred_types }, + [NL_UNION_TCA_OPTION_DATA_HHF] = { .count = ELEMENTSOF(rtnl_tca_option_data_hhf_types), + .types = rtnl_tca_option_data_hhf_types }, + [NL_UNION_TCA_OPTION_DATA_HTB] = { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types), + .types = rtnl_tca_option_data_htb_types }, + [NL_UNION_TCA_OPTION_DATA_PIE] = { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types), + .types = rtnl_tca_option_data_pie_types }, + [NL_UNION_TCA_OPTION_DATA_QFQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_qfq_types), + .types = rtnl_tca_option_data_qfq_types }, + [NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types), + .types = rtnl_tca_option_data_sfb_types }, [NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types), .types = rtnl_tca_option_data_tbf_types }, }; @@ -818,16 +956,16 @@ static const NLTypeSystemUnion rtnl_tca_option_data_type_system_union = { .match = TCA_KIND, }; -static const NLType rtnl_qdisc_types[] = { +static const NLType rtnl_tca_types[] = { [TCA_KIND] = { .type = NETLINK_TYPE_STRING }, [TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union }, [TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 }, [TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 }, }; -static const NLTypeSystem rtnl_qdisc_type_system = { - .count = ELEMENTSOF(rtnl_qdisc_types), - .types = rtnl_qdisc_types, +static const NLTypeSystem rtnl_tca_type_system = { + .count = ELEMENTSOF(rtnl_tca_types), + .types = rtnl_tca_types, }; static const NLType error_types[] = { @@ -868,9 +1006,12 @@ static const NLType rtnl_types[] = { [RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) }, [RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) }, [RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) }, - [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) }, - [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) }, - [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) }, + [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, + [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, + [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, + [RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, + [RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, + [RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) }, }; const NLTypeSystem rtnl_type_system_root = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index b2fa8c96e..058747a2e 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -9,6 +9,10 @@ enum { NETLINK_TYPE_U16, /* NLA_U16 */ NETLINK_TYPE_U32, /* NLA_U32 */ NETLINK_TYPE_U64, /* NLA_U64 */ + NETLINK_TYPE_S8, /* NLA_S8 */ + NETLINK_TYPE_S16, /* NLA_S16 */ + NETLINK_TYPE_S32, /* NLA_S32 */ + NETLINK_TYPE_S64, /* NLA_S64 */ NETLINK_TYPE_STRING, /* NLA_STRING */ NETLINK_TYPE_FLAG, /* NLA_FLAG */ NETLINK_TYPE_IN_ADDR, @@ -92,9 +96,18 @@ const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; typedef enum NLUnionTCAOptionData { + NL_UNION_TCA_OPTION_DATA_CAKE, NL_UNION_TCA_OPTION_DATA_CODEL, + NL_UNION_TCA_OPTION_DATA_DRR, + NL_UNION_TCA_OPTION_DATA_ETS, NL_UNION_TCA_OPTION_DATA_FQ, NL_UNION_TCA_OPTION_DATA_FQ_CODEL, + NL_UNION_TCA_OPTION_DATA_GRED, + NL_UNION_TCA_OPTION_DATA_HHF, + NL_UNION_TCA_OPTION_DATA_HTB, + NL_UNION_TCA_OPTION_DATA_PIE, + NL_UNION_TCA_OPTION_DATA_QFQ, + NL_UNION_TCA_OPTION_DATA_SFB, NL_UNION_TCA_OPTION_DATA_TBF, _NL_UNION_TCA_OPTION_DATA_MAX, _NL_UNION_TCA_OPTION_DATA_INVALID = -1, diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 7387cffaa..f045e794b 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -2,6 +2,7 @@ #include "sd-netlink.h" +#include "format-util.h" #include "memory-util.h" #include "netlink-internal.h" #include "netlink-util.h" @@ -9,6 +10,8 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + _cleanup_strv_free_ char **alternative_names = NULL; + char old_name[IF_NAMESIZE + 1] = {}; int r; assert(rtnl); @@ -18,10 +21,18 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { if (!ifname_valid(name)) return -EINVAL; - if (!*rtnl) { - r = sd_netlink_open(rtnl); + r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names); + if (r < 0) + log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m", + ifindex); + + if (strv_contains(alternative_names, name)) { + r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); if (r < 0) - return r; + return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m", + name, ifindex); + + format_ifname(ifindex, old_name); } r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); @@ -36,6 +47,13 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { if (r < 0) return r; + if (!isempty(old_name)) { + r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name)); + if (r < 0) + log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m", + old_name, ifindex); + } + return 0; } @@ -85,12 +103,45 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, return 0; } -int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) { +int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; + _cleanup_strv_free_ char **names = NULL; + int r; + + assert(rtnl); + assert(ifindex > 0); + assert(ret); + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex); + if (r < 0) + return r; + + r = sd_netlink_call(*rtnl, message, 0, &reply); + if (r < 0) + return r; + + r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names); + if (r < 0 && r != -ENODATA) + return r; + + *ret = TAKE_PTR(names); + + return 0; +} + +static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; int r; assert(rtnl); assert(ifindex > 0); + assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP)); if (strv_isempty(alternative_names)) return 0; @@ -101,7 +152,7 @@ int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const return r; } - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, ifindex); + r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex); if (r < 0) return r; @@ -124,6 +175,14 @@ int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const return 0; } +int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) { + return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names); +} + +int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) { + return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names); +} + int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; int r; @@ -236,10 +295,10 @@ int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t seria if (r < 0) return r; + rtnl_message_seal(*ret); (*ret)->hdr->nlmsg_seq = serial; err = NLMSG_DATA((*ret)->hdr); - err->error = error; return 0; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index d2d8334b2..04e6a98e6 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -47,10 +47,16 @@ 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); +} + int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu); +int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret); int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names); int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names); +int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names); int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name); int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 182a66674..7689bf662 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -1077,3 +1077,46 @@ int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t 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(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; + + return 0; +} + +int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) { + struct tcmsg *tcm; + + assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); + + tcm = NLMSG_DATA(m->hdr); + tcm->tcm_parent = parent; + + return 0; +} + +int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) { + struct tcmsg *tcm; + + assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); + + tcm = NLMSG_DATA(m->hdr); + tcm->tcm_handle = handle; + + return 0; +} diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 5b7081089..91670ee32 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "hashmap.h" +#include "io-util.h" #include "macro.h" #include "netlink-internal.h" #include "netlink-slot.h" @@ -462,8 +463,6 @@ static usec_t calc_elapse(uint64_t usec) { } static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) { - struct pollfd p[1] = {}; - struct timespec ts; usec_t m = USEC_INFINITY; int r, e; @@ -492,17 +491,16 @@ static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) { } } - if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) + if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m)) m = timeout_usec; - p[0].fd = rtnl->fd; - p[0].events = e; - - r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + r = fd_wait_for_event(rtnl->fd, e, m); if (r < 0) - return -errno; + return r; + if (r == 0) + return 0; - return r > 0 ? 1 : 0; + return 1; } int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) { diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index b9b618e58..ce6ae846c 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -168,6 +168,14 @@ _public_ int sd_network_link_get_address_state(int ifindex, char **state) { return network_link_get_string(ifindex, "ADDRESS_STATE", state); } +_public_ int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid) { + return network_link_get_string(ifindex, "DHCP6_CLIENT_IAID", iaid); +} + +_public_ int sd_network_link_get_dhcp6_client_duid_string(int ifindex, char **duid) { + return network_link_get_string(ifindex, "DHCP6_CLIENT_DUID", duid); +} + _public_ int sd_network_link_get_required_for_online(int ifindex) { _cleanup_free_ char *s = NULL; int r; @@ -224,14 +232,6 @@ _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); } -_public_ int sd_network_link_get_timezone(int ifindex, char **ret) { - return network_link_get_string(ifindex, "TIMEZONE", ret); -} - -_public_ int sd_network_link_get_dhcp4_address(int ifindex, char **ret) { - return network_link_get_string(ifindex, "DHCP4_ADDRESS", ret); -} - _public_ int sd_network_link_get_dns(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "DNS", ret); } @@ -240,6 +240,10 @@ _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "NTP", ret); } +_public_ int sd_network_link_get_sip(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "SIP", ret); +} + _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "DOMAINS", ret); } @@ -248,10 +252,6 @@ _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); } -_public_ int sd_network_link_get_sip_servers(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "SIP", ret); -} - _public_ int sd_network_link_get_dns_default_route(int ifindex) { char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_free_ char *s = NULL; diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index 95d6551e5..736795d1d 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -7,6 +7,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "path-lookup.h" #include "path-util.h" #include "string-util.h" #include "strv.h" @@ -318,64 +319,130 @@ static int get_path(uint64_t type, char **buffer, const char **ret) { case SD_PATH_USER_DESKTOP: return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); + + case SD_PATH_SYSTEMD_UTIL: + *ret = ROOTPREFIX "/lib/systemd"; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_UNIT: + *ret = SYSTEM_DATA_UNIT_PATH; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_PRESET: + *ret = ROOTPREFIX "/lib/systemd/system-preset"; + return 0; + + case SD_PATH_SYSTEMD_USER_UNIT: + *ret = USER_DATA_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_PRESET: + *ret = ROOTPREFIX "/lib/systemd/user-preset"; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_CONF: + *ret = SYSTEM_CONFIG_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_CONF: + *ret = USER_CONFIG_UNIT_DIR; + return 0; + + case SD_PATH_SYSTEMD_SYSTEM_GENERATOR: + *ret = SYSTEM_GENERATOR_DIR; + return 0; + + case SD_PATH_SYSTEMD_USER_GENERATOR: + *ret = USER_GENERATOR_DIR; + return 0; + + case SD_PATH_SYSTEMD_SLEEP: + *ret = ROOTPREFIX "/lib/systemd/system-sleep"; + return 0; + + case SD_PATH_SYSTEMD_SHUTDOWN: + *ret = ROOTPREFIX "/lib/systemd/system-shutdown"; + return 0; + + /* FIXME: systemd.pc uses ${prefix}, but CONF_PATHS_NULSTR doesn't. + * Should ${prefix} use in systemd.pc be removed? */ + case SD_PATH_TMPFILES: + *ret = "/usr/lib/tmpfiles.d"; + return 0; + + case SD_PATH_SYSUSERS: + *ret = ROOTPREFIX "/lib/sysusers.d"; + return 0; + + case SD_PATH_SYSCTL: + *ret = ROOTPREFIX "/lib/sysctl.d"; + return 0; + + case SD_PATH_BINFMT: + *ret = ROOTPREFIX "/lib/binfmt.d"; + return 0; + + case SD_PATH_MODULES_LOAD: + *ret = ROOTPREFIX "/lib/modules-load.d"; + return 0; + + case SD_PATH_CATALOG: + *ret = "/usr/lib/systemd/catalog"; + return 0; } return -EOPNOTSUPP; } -_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { +static int get_path_alloc(uint64_t type, const char *suffix, char **path) { _cleanup_free_ char *buffer = NULL; + char *buffer2 = NULL; const char *ret; - char *cc; int r; - assert_return(path, -EINVAL); - - if (IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_BINARIES_DEFAULT, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { - - _cleanup_strv_free_ char **l = NULL; - - r = sd_path_search(type, suffix, &l); - if (r < 0) - return r; - - buffer = strv_join(l, ":"); - if (!buffer) - return -ENOMEM; - - *path = TAKE_PTR(buffer); - return 0; - } + assert(path); r = get_path(type, &buffer, &ret); if (r < 0) return r; - if (!suffix) { - if (!buffer) { - buffer = strdup(ret); - if (!buffer) - return -ENOMEM; - } - - *path = TAKE_PTR(buffer); - return 0; + if (suffix) { + suffix += strspn(suffix, "/"); + buffer2 = path_join(ret, suffix); + if (!buffer2) + return -ENOMEM; + } else if (!buffer) { + buffer = strdup(ret); + if (!buffer) + return -ENOMEM; } - suffix += strspn(suffix, "/"); - cc = path_join(ret, suffix); - if (!cc) + *path = buffer2 ?: TAKE_PTR(buffer); + return 0; +} + +_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) { + int r; + + assert_return(path, -EINVAL); + + r = get_path_alloc(type, suffix, path); + if (r != -EOPNOTSUPP) + return r; + + /* Fall back to sd_path_lookup_strv */ + _cleanup_strv_free_ char **l = NULL; + char *buffer; + + r = sd_path_lookup_strv(type, suffix, &l); + if (r < 0) + return r; + + buffer = strv_join(l, ":"); + if (!buffer) return -ENOMEM; - *path = TAKE_PTR(cc); + *path = buffer; return 0; } @@ -454,6 +521,7 @@ static int search_from_environment( #endif static int get_search(uint64_t type, char ***list) { + int r; assert(list); @@ -536,58 +604,69 @@ static int get_search(uint64_t type, char ***list) { "/etc", NULL); - case SD_PATH_SEARCH_BINARIES_DEFAULT: { - char **t; + case SD_PATH_SEARCH_BINARIES_DEFAULT: + return strv_from_nulstr(list, DEFAULT_PATH_NULSTR); - t = strv_split_nulstr(DEFAULT_PATH_NULSTR); + 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; + + r = lookup_paths_init(&lp, scope, 0, NULL); + if (r < 0) + return r; + + *list = TAKE_PTR(lp.search_path); + return 0; + } + + 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; + + t = generator_binary_paths(scope); if (!t) return -ENOMEM; *list = t; return 0; - }} + } + + case SD_PATH_SYSTEMD_SEARCH_NETWORK: + return strv_from_nulstr(list, NETWORK_DIRS_NULSTR); + + } return -EOPNOTSUPP; } -_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { - char **i, **j; +_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) { _cleanup_strv_free_ char **l = NULL, **n = NULL; int r; assert_return(paths, -EINVAL); - if (!IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_BINARIES_DEFAULT, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { + r = get_search(type, &l); + if (r == -EOPNOTSUPP) { + _cleanup_free_ char *t = NULL; - char *p; - - r = sd_path_home(type, suffix, &p); + r = get_path_alloc(type, suffix, &t); if (r < 0) return r; l = new(char*, 2); - if (!l) { - free(p); + if (!l) return -ENOMEM; - } - - l[0] = p; + l[0] = TAKE_PTR(t); l[1] = NULL; *paths = TAKE_PTR(l); return 0; - } - r = get_search(type, &l); - if (r < 0) + } else if (r < 0) return r; if (!suffix) { @@ -599,7 +678,7 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { if (!n) return -ENOMEM; - j = n; + char **i, **j = n; STRV_FOREACH(i, l) { *j = path_join(*i, suffix); if (!*j) @@ -607,8 +686,8 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { j++; } - *j = NULL; + *paths = TAKE_PTR(n); return 0; } diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index 5f780e0be..5bec7418b 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -9,6 +9,7 @@ #include "device-monitor-private.h" #include "device-private.h" #include "device-util.h" +#include "io-util.h" #include "libudev-device-internal.h" #include "string-util.h" @@ -191,17 +192,11 @@ _public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) { } static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_device **ret) { - struct pollfd pfd; int r; assert(udev_monitor); assert(ret); - pfd = (struct pollfd) { - .fd = device_monitor_get_fd(udev_monitor->monitor), - .events = POLLIN, - }; - for (;;) { /* r == 0 means a device is received but it does not pass the current filter. */ r = device_monitor_receive_device(udev_monitor->monitor, ret); @@ -209,17 +204,18 @@ static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_ return r; for (;;) { - /* wait next message */ - r = poll(&pfd, 1, 0); + /* Wait for next message */ + r = fd_wait_for_event(device_monitor_get_fd(udev_monitor->monitor), POLLIN, 0); if (r < 0) { - if (IN_SET(errno, EINTR, EAGAIN)) + if (IN_SET(r, -EINTR, -EAGAIN)) continue; - return -errno; - } else if (r == 0) + return r; + } + if (r == 0) return -EAGAIN; - /* receive next message */ + /* Receive next message */ break; } } diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index 37660d031..4a471fb90 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -154,8 +154,8 @@ size_t util_replace_whitespace(const char *str, char *to, size_t len) { return j; } -/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ -size_t util_replace_chars(char *str, const char *white) { +/* allow chars in allow list, plain ascii, hex-escaping and valid utf8 */ +size_t util_replace_chars(char *str, const char *allow) { size_t i = 0, replaced = 0; assert(str); @@ -163,7 +163,7 @@ size_t util_replace_chars(char *str, const char *white) { while (str[i] != '\0') { int len; - if (whitelisted_char_for_devnode(str[i], white)) { + if (allow_listed_char_for_devnode(str[i], allow)) { i++; continue; } @@ -182,7 +182,7 @@ size_t util_replace_chars(char *str, const char *white) { } /* if space is allowed, replace whitespace with ordinary space */ - if (isspace(str[i]) && white && strchr(white, ' ')) { + if (isspace(str[i]) && allow && strchr(allow, ' ')) { str[i] = ' '; i++; replaced++; diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c index b4450727c..aa187b2b4 100644 --- a/src/libudev/libudev.c +++ b/src/libudev/libudev.c @@ -106,7 +106,7 @@ _public_ struct udev *udev_unref(struct udev *udev) { assert(udev->n_ref > 0); udev->n_ref--; if (udev->n_ref > 0) - /* This is different from our convetion, but let's keep backward + /* This is different from our convention, but let's keep backward * compatibility. So, do not use DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC() * macro to define this function. */ return udev; diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index 30669a935..233d08130 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -267,9 +267,8 @@ int x11_read_data(Context *c, sd_bus_message *m) { else if (streq(a[1], "XkbOptions")) p = &c->x11_options; - if (p) { + if (p) free_and_replace(*p, a[2]); - } } } else if (!in_section && first_word(l, "Section")) { diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 6f2d37d22..e0664de82 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -8,7 +8,8 @@ #include "sd-bus.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-locator.h" +#include "bus-map-properties.h" #include "fd-util.h" #include "fileio.h" #include "kbd-util.h" @@ -163,13 +164,7 @@ static int set_locale(int argc, char **argv, void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "SetLocale"); + r = bus_message_new_method_call(bus, &m, bus_locale, "SetLocale"); if (r < 0) return bus_log_create_error(r); @@ -215,11 +210,9 @@ static int set_vconsole_keymap(int argc, char **argv, void *userdata) { map = argv[1]; toggle_map = argc > 2 ? argv[2] : ""; - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", + bus_locale, "SetVConsoleKeyboard", &error, NULL, @@ -258,11 +251,9 @@ static int set_x11_keymap(int argc, char **argv, void *userdata) { variant = argc > 3 ? argv[3] : ""; options = argc > 4 ? argv[4] : ""; - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", + bus_locale, "SetX11Keyboard", &error, NULL, @@ -512,9 +503,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) @@ -522,7 +511,7 @@ static int run(int argc, char *argv[]) { r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); return localectl_main(bus, argc, argv); } diff --git a/src/locale/localed.c b/src/locale/localed.c index 09f16d25f..715ce5cac 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -14,9 +14,11 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-log-control-api.h" #include "bus-message.h" #include "bus-polkit.h" #include "def.h" +#include "dlfcn-util.h" #include "keymap-util.h" #include "locale-util.h" #include "macro.h" @@ -24,6 +26,7 @@ #include "missing_capability.h" #include "path-util.h" #include "selinux-util.h" +#include "service-util.h" #include "signal-util.h" #include "string-util.h" #include "strv.h" @@ -256,18 +259,57 @@ static int property_get_xkb( return -EINVAL; } +static int process_locale_list_item( + const char *assignment, + char *new_locale[static _VARIABLE_LC_MAX], + sd_bus_error *error) { + + assert(assignment); + assert(new_locale); + + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { + const char *name, *e; + + assert_se(name = locale_variable_to_string(p)); + + e = startswith(assignment, name); + if (!e) + continue; + + if (*e != '=') + continue; + + e++; + + if (!locale_is_valid(e)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e); + if (locale_is_installed(e) <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e); + if (new_locale[p]) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name); + + new_locale[p] = strdup(e); + if (!new_locale[p]) + return -ENOMEM; + + return 0; + } + + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment); +} + static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {}; _cleanup_strv_free_ char **settings = NULL, **l = NULL; Context *c = userdata; bool modified = false; - int interactive, p, r; + int interactive, r; char **i; assert(m); assert(c); - r = bus_message_read_strv_extend(m, &l); + r = sd_bus_message_read_strv(m, &l); if (r < 0) return r; @@ -276,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er return r; /* If single locale without variable name is provided, then we assume it is LANG=. */ - if (strv_length(l) == 1 && !strchr(*l, '=')) { - if (!locale_is_valid(*l)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); + if (strv_length(l) == 1 && !strchr(l[0], '=')) { + if (!locale_is_valid(l[0])) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]); + if (locale_is_installed(l[0]) <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]); - new_locale[VARIABLE_LANG] = strdup(*l); + new_locale[VARIABLE_LANG] = strdup(l[0]); if (!new_locale[VARIABLE_LANG]) return -ENOMEM; @@ -289,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er /* Check whether a variable is valid */ STRV_FOREACH(i, l) { - bool valid = false; - - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - size_t k; - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - k = strlen(name); - if (startswith(*i, name) && - (*i)[k] == '=' && - locale_is_valid((*i) + k + 1)) { - valid = true; - - new_locale[p] = strdup((*i) + k + 1); - if (!new_locale[p]) - return -ENOMEM; - - break; - } - } - - if (!valid) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); + r = process_locale_list_item(*i, new_locale, error); + if (r < 0) + return r; } /* If LANG was specified, but not LANGUAGE, check if we should @@ -336,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er } /* Merge with the current settings */ - for (p = 0; p < _VARIABLE_LC_MAX; p++) + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) if (!isempty(c->locale[p]) && isempty(new_locale[p])) { new_locale[p] = strdup(c->locale[p]); if (!new_locale[p]) @@ -345,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er locale_simplify(new_locale); - for (p = 0; p < _VARIABLE_LC_MAX; p++) + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) if (!streq_ptr(c->locale[p], new_locale[p])) { modified = true; break; @@ -370,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - for (p = 0; p < _VARIABLE_LC_MAX; p++) + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) free_and_replace(c->locale[p], new_locale[p]); r = locale_write_data(c, &settings); @@ -478,10 +500,9 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char const char *fmt; fmt = strjoina("libxkbcommon: ", format); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" + DISABLE_WARNING_FORMAT_NONLITERAL; log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); -#pragma GCC diagnostic pop + REENABLE_WARNING; } #define LOAD_SYMBOL(symbol, dl, name) \ @@ -510,7 +531,7 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v }; struct xkb_context *ctx = NULL; struct xkb_keymap *km = NULL; - void *dl; + _cleanup_(dlclosep) void *dl = NULL; int r; /* Compile keymap from RMLVO information to check out its validity */ @@ -562,7 +583,6 @@ finish: if (symbol_xkb_context_unref && ctx) symbol_xkb_context_unref(ctx); - (void) dlclose(dl); return r; } @@ -676,12 +696,44 @@ static const sd_bus_vtable locale_vtable[] = { SD_BUS_PROPERTY("X11Options", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("VConsoleKeymap", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD_WITH_NAMES("SetLocale", + "asb", + SD_BUS_PARAM(locale) + SD_BUS_PARAM(interactive), + NULL,, + method_set_locale, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetVConsoleKeyboard", + "ssbb", + SD_BUS_PARAM(keymap) + SD_BUS_PARAM(keymap_toggle) + SD_BUS_PARAM(convert) + SD_BUS_PARAM(interactive), + NULL,, + method_set_vc_keyboard, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetX11Keyboard", + "ssssbb", + SD_BUS_PARAM(layout) + SD_BUS_PARAM(model) + SD_BUS_PARAM(variant) + SD_BUS_PARAM(options) + SD_BUS_PARAM(convert) + SD_BUS_PARAM(interactive), + NULL,, + method_set_x11_keyboard, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END }; +static const BusObjectImplementation manager_object = { + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + .vtables = BUS_VTABLES(locale_vtable), +}; + static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -694,9 +746,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to get system bus connection: %m"); - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c); + r = bus_add_implementation(bus, &manager_object, c); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); + return r; + + r = bus_log_control_api_register(bus); + if (r < 0) + return r; r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL); if (r < 0) @@ -723,11 +779,19 @@ static int run(int argc, char *argv[]) { log_setup_service(); - umask(0022); - mac_selinux_init(); + r = service_parse_argv("systemd-localed.service", + "Manage system locale settings and key mappings.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); + umask(0022); + + r = mac_selinux_init(); + if (r < 0) + return r; assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); diff --git a/src/locale/meson.build b/src/locale/meson.build index e87a10ebe..314b0a3d3 100644 --- a/src/locale/meson.build +++ b/src/locale/meson.build @@ -34,5 +34,5 @@ tests += [ 'src/locale/keymap-util.c', 'src/locale/keymap-util.h'], [libshared], - [libdl]], + []], ] diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in index 2bbd18363..60dd6add6 100644 --- a/src/login/71-seat.rules.in +++ b/src/login/71-seat.rules.in @@ -30,6 +30,11 @@ SUBSYSTEM=="pci", ENV{ID_PCI_CLASS_FROM_DATABASE}=="Display controller", \ ENV{DRIVER}=="", IMPORT{cmdline}="nomodeset", TAG+="seat", TAG+="master-of-seat" SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" + +# Allow individual USB ports to be assigned to a seat +SUBSYSTEM=="usb", ATTR{bDeviceClass}=="00", TAG+="seat" + +# Allow USB hubs (and all downstream ports) to be assigned to a seat SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" # 'Plugable' USB hub, sound, network, graphics adapter diff --git a/src/login/inhibit.c b/src/login/inhibit.c index a1602031b..e3866eee5 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -132,13 +132,13 @@ static int print_inhibitors(sd_bus *bus) { if (table_get_rows(table) > 1) { r = table_set_sort(table, (size_t) 1, (size_t) 0, (size_t) 5, (size_t) 6, (size_t) -1); if (r < 0) - return log_error_errno(r, "Failed to sort table: %m"); + return table_log_sort_error(r); table_set_header(table, arg_legend); r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } if (arg_legend) { diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 2e39f5576..4297a5100 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -9,8 +9,10 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-print-properties.h" #include "bus-unit-procs.h" -#include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" #include "format-table.h" @@ -64,14 +66,7 @@ static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *e int r; char *ans; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "GetSession", - error, &reply, - "s", session_id); + r = bus_call_method(bus, bus_login_mgr, "GetSession", error, &reply, "s", session_id); if (r < 0) return r; @@ -96,7 +91,7 @@ static int show_table(Table *table, const char *word) { if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) - return log_error_errno(r, "Failed to sort table: %m"); + return table_log_sort_error(r); table_set_header(table, arg_legend); @@ -105,7 +100,7 @@ static int show_table(Table *table, const char *word) { else r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } if (arg_legend) { @@ -130,14 +125,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSessions", - &error, &reply, - NULL); + r = bus_call_method(bus, bus_login_mgr, "ListSessions", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list sessions: %s", bus_error_message(&error, r)); @@ -211,14 +199,7 @@ static int list_users(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListUsers", - &error, &reply, - NULL); + r = bus_call_method(bus, bus_login_mgr, "ListUsers", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list users: %s", bus_error_message(&error, r)); @@ -268,14 +249,7 @@ static int list_seats(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSeats", - &error, &reply, - NULL); + r = bus_call_method(bus, bus_login_mgr, "ListSeats", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list seats: %s", bus_error_message(&error, r)); @@ -825,7 +799,15 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) { *new_line = true; - r = bus_print_all_properties(bus, "org.freedesktop.login1", path, print_property, arg_property, arg_value, arg_all, NULL); + r = bus_print_all_properties( + bus, + "org.freedesktop.login1", + path, + print_property, + arg_property, + arg_value, + arg_all, + NULL); if (r < 0) return bus_log_parse_error(r); @@ -835,7 +817,7 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) { static int show_session(int argc, char *argv[], void *userdata) { bool properties, new_line = false; sd_bus *bus = userdata; - int r, i; + int r; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *path = NULL; @@ -854,7 +836,7 @@ static int show_session(int argc, char *argv[], void *userdata) { return print_session_status_info(bus, "/org/freedesktop/login1/session/auto", &new_line); } - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { r = get_session_path(bus, argv[i], &error, &path); if (r < 0) return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r)); @@ -874,7 +856,7 @@ static int show_session(int argc, char *argv[], void *userdata) { static int show_user(int argc, char *argv[], void *userdata) { bool properties, new_line = false; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); @@ -891,7 +873,7 @@ static int show_user(int argc, char *argv[], void *userdata) { return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line); } - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { _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 *path = NULL; @@ -931,7 +913,7 @@ static int show_user(int argc, char *argv[], void *userdata) { static int show_seat(int argc, char *argv[], void *userdata) { bool properties, new_line = false; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); @@ -948,19 +930,12 @@ static int show_seat(int argc, char *argv[], void *userdata) { return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto", &new_line); } - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { _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 *path = NULL; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "GetSeat", - &error, &reply, - "s", argv[i]); + r = bus_call_method(bus, bus_login_mgr, "GetSeat", &error, &reply, "s", argv[i]); if (r < 0) return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r)); @@ -983,7 +958,7 @@ static int show_seat(int argc, char *argv[], void *userdata) { static int activate(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); @@ -1007,13 +982,11 @@ static int activate(int argc, char *argv[], void *userdata) { return 0; } - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + bus_login_mgr, streq(argv[0], "lock-session") ? "LockSession" : streq(argv[0], "unlock-session") ? "UnlockSession" : streq(argv[0], "terminate-session") ? "TerminateSession" : @@ -1030,7 +1003,7 @@ static int activate(int argc, char *argv[], void *userdata) { static int kill_session(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); @@ -1040,16 +1013,14 @@ static int kill_session(int argc, char *argv[], void *userdata) { if (!arg_kill_who) arg_kill_who = "all"; - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "KillSession", - &error, NULL, - "ssi", argv[i], arg_kill_who, arg_signal); + r = bus_call_method( + bus, + bus_login_mgr, + "KillSession", + &error, NULL, + "ssi", argv[i], arg_kill_who, arg_signal); if (r < 0) return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, -r)); } @@ -1062,7 +1033,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) { sd_bus *bus = userdata; char* short_argv[3]; bool b; - int r, i; + int r; assert(bus); assert(argv); @@ -1082,7 +1053,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) { argc = 2; } - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { uid_t uid; if (isempty(argv[i])) @@ -1093,14 +1064,12 @@ static int enable_linger(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); } - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetUserLinger", - &error, NULL, - "ubb", (uint32_t) uid, b, true); + r = bus_call_method( + bus, + bus_login_mgr, + "SetUserLinger", + &error, NULL, + "ubb", (uint32_t) uid, b, true); if (r < 0) return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, -r)); } @@ -1111,28 +1080,21 @@ static int enable_linger(int argc, char *argv[], void *userdata) { static int terminate_user(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { uid_t uid; r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "TerminateUser", - &error, NULL, - "u", (uint32_t) uid); + r = bus_call_method(bus, bus_login_mgr, "TerminateUser", &error, NULL, "u", (uint32_t) uid); if (r < 0) return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, -r)); } @@ -1143,7 +1105,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) { static int kill_user(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); @@ -1153,18 +1115,16 @@ static int kill_user(int argc, char *argv[], void *userdata) { if (!arg_kill_who) arg_kill_who = "all"; - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { uid_t uid; r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + bus_login_mgr, "KillUser", &error, NULL, "ui", (uint32_t) uid, arg_signal); @@ -1178,24 +1138,21 @@ static int kill_user(int argc, char *argv[], void *userdata) { static int attach(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - for (i = 2; i < argc; i++) { + for (int i = 2; i < argc; i++) { - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + bus_login_mgr, "AttachDevice", &error, NULL, "ssb", argv[1], argv[i], true); - if (r < 0) return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, -r)); } @@ -1213,14 +1170,7 @@ static int flush_devices(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "FlushDevices", - &error, NULL, - "b", true); + r = bus_call_method(bus, bus_login_mgr, "FlushDevices", &error, NULL, "b", true); if (r < 0) return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, -r)); @@ -1237,11 +1187,9 @@ static int lock_sessions(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + bus_login_mgr, streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions", &error, NULL, NULL); @@ -1254,23 +1202,16 @@ static int lock_sessions(int argc, char *argv[], void *userdata) { static int terminate_seat(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - int r, i; + int r; assert(bus); assert(argv); polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "TerminateSeat", - &error, NULL, - "s", argv[i]); + r = bus_call_method(bus, bus_login_mgr, "TerminateSeat", &error, NULL, "s", argv[i]); if (r < 0) return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, -r)); } @@ -1347,7 +1288,6 @@ static int help(int argc, char *argv[], void *userdata) { } static int parse_argv(int argc, char *argv[]) { - enum { ARG_VERSION = 0x100, ARG_VALUE, @@ -1486,7 +1426,6 @@ static int parse_argv(int argc, char *argv[]) { } static int loginctl_main(int argc, char *argv[], sd_bus *bus) { - static const Verb verbs[] = { { "help", VERB_ANY, VERB_ANY, 0, help }, { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions }, @@ -1523,9 +1462,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); /* The journal merging logic potentially needs a lot of fds. */ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); @@ -1538,7 +1475,7 @@ static int run(int argc, char *argv[]) { r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c index ff192c53e..76af208af 100644 --- a/src/login/logind-acl.c +++ b/src/login/logind-acl.c @@ -206,7 +206,7 @@ int devnode_acl_all(const char *seat, continue; log_device_debug(d, "Found udev node %s for seat %s", node, seat); - r = set_put_strdup(nodes, node); + r = set_put_strdup(&nodes, node); if (r < 0) return r; } diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c index 3f4b65e1f..450ec3204 100644 --- a/src/login/logind-brightness.c +++ b/src/login/logind-brightness.c @@ -174,15 +174,11 @@ static int set_add_message(Set **set, sd_bus_message *message) { if (r <= 0) return r; - r = set_ensure_allocated(set, &bus_message_hash_ops); - if (r < 0) + r = set_ensure_put(set, &bus_message_hash_ops, message); + if (r <= 0) return r; - - r = set_put(*set, message); - if (r < 0) - return r; - sd_bus_message_ref(message); + return 1; } diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 22a42b077..480ec1927 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -4,9 +4,6 @@ #include #include #include -#if ENABLE_UTMP -#include -#endif #include "sd-device.h" @@ -16,6 +13,7 @@ #include "cgroup-util.h" #include "conf-parser.h" #include "device-util.h" +#include "efi-loader.h" #include "errno-util.h" #include "fd-util.h" #include "limits-util.h" @@ -28,6 +26,7 @@ #include "udev-util.h" #include "user-util.h" #include "userdb.h" +#include "utmp-wtmp.h" void manager_reset_config(Manager *m) { assert(m); @@ -55,6 +54,7 @@ void manager_reset_config(Manager *m) { m->idle_action = HANDLE_IGNORE; m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */ + m->runtime_dir_inodes = DIV_ROUND_UP(m->runtime_dir_size, 4096); /* 4k per inode */ m->sessions_max = 8192; m->inhibitors_max = 8192; @@ -67,11 +67,13 @@ void manager_reset_config(Manager *m) { int manager_parse_config_file(Manager *m) { assert(m); - return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf", - CONF_PATHS_NULSTR("systemd/logind.conf.d"), - "Login\0", - config_item_perf_lookup, logind_gperf_lookup, - CONFIG_PARSE_WARN, m); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/logind.conf", + CONF_PATHS_NULSTR("systemd/logind.conf.d"), + "Login\0", + config_item_perf_lookup, logind_gperf_lookup, + CONFIG_PARSE_WARN, m, + NULL); } int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device) { @@ -171,7 +173,7 @@ int manager_add_user_by_name( assert(m); assert(name); - r = userdb_by_name(name, 0, &ur); + r = userdb_by_name(name, USERDB_AVOID_SHADOW, &ur); if (r < 0) return r; @@ -189,7 +191,7 @@ int manager_add_user_by_uid( assert(m); assert(uid_is_valid(uid)); - r = userdb_by_uid(uid, 0, &ur); + r = userdb_by_uid(uid, USERDB_AVOID_SHADOW, &ur); if (r < 0) return r; @@ -597,10 +599,10 @@ static int manager_count_external_displays(Manager *m) { if (sd_device_get_sysname(d, &nn) < 0) continue; - /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash separated item - * (the first is the card name, the last the connector number). We implement a blacklist of external - * displays here, rather than a whitelist of internal ones, to ensure we don't block suspends too - * eagerly. */ + /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash + * separated item (the first is the card name, the last the connector number). We implement a + * deny list of external displays here, rather than an allow list of internal ones, to ensure + * we don't block suspends too eagerly. */ dash = strchr(nn, '-'); if (!dash) continue; @@ -681,13 +683,14 @@ bool manager_all_buttons_ignored(Manager *m) { int manager_read_utmp(Manager *m) { #if ENABLE_UTMP int r; + _cleanup_(utxent_cleanup) bool utmpx = false; assert(m); if (utmpxname(_PATH_UTMPX) < 0) return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m"); - setutxent(); + utmpx = utxent_start(); for (;;) { _cleanup_free_ char *t = NULL; @@ -700,8 +703,7 @@ int manager_read_utmp(Manager *m) { if (!u) { if (errno != 0) log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m"); - r = 0; - break; + return 0; } if (u->ut_type != USER_PROCESS) @@ -711,18 +713,14 @@ int manager_read_utmp(Manager *m) { continue; t = strndup(u->ut_line, sizeof(u->ut_line)); - if (!t) { - r = log_oom(); - break; - } + if (!t) + return log_oom(); c = path_startswith(t, "/dev/"); if (c) { r = free_and_strdup(&t, c); - if (r < 0) { - log_oom(); - break; - } + if (r < 0) + return log_oom(); } if (isempty(t)) @@ -752,8 +750,6 @@ int manager_read_utmp(Manager *m) { log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id); } - endutxent(); - return r; #else return 0; #endif @@ -816,3 +812,27 @@ void manager_reconnect_utmp(Manager *m) { manager_connect_utmp(m); #endif } + +int manager_read_efi_boot_loader_entries(Manager *m) { +#if ENABLE_EFI + int r; + + assert(m); + if (m->efi_boot_loader_entries_set) + return 0; + + r = efi_loader_get_entries(&m->efi_boot_loader_entries); + if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r)) { + log_debug_errno(r, "Boot loader reported no entries."); + m->efi_boot_loader_entries_set = true; + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to determine entries reported by boot loader: %m"); + + m->efi_boot_loader_entries_set = true; + return 1; +#else + return 0; +#endif +} diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 762730d12..75a48300a 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -12,6 +12,8 @@ #include "bootspec.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-get-properties.h" +#include "bus-locator.h" #include "bus-polkit.h" #include "bus-unit-util.h" #include "bus-util.h" @@ -885,7 +887,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus if (r < 0) goto fail; - session->type = t; + session->original_type = session->type = t; session->class = c; session->remote = remote; session->vtnr = vtnr; @@ -1625,11 +1627,9 @@ static int execute_shutdown_or_sleep( if (w == INHIBIT_SHUTDOWN) bus_manager_log_shutdown(m, unit_name); - r = sd_bus_call_method( + r = bus_call_method( m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "StartUnit", error, &reply, @@ -2753,8 +2753,6 @@ static int property_get_reboot_to_boot_loader_menu( r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); if (r == -ENXIO) { - _cleanup_free_ char *v = NULL; - /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished: * * 1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user) @@ -2762,20 +2760,10 @@ static int property_get_reboot_to_boot_loader_menu( * 3. Variable set to numeric value formatted in ASCII, boot into boot loader menu with the specified timeout in seconds */ - r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v); + r = efi_loader_get_config_timeout_one_shot(&x); if (r < 0) { if (r != -ENOENT) - log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable: %m"); - } else { - uint64_t sec; - - r = safe_atou64(v, &sec); - if (r < 0) - log_warning_errno(r, "Failed to parse LoaderConfigTimeoutOneShot value '%s': %m", v); - else if (sec > (USEC_INFINITY / USEC_PER_SEC)) - log_warning("LoaderConfigTimeoutOneShot too large, ignoring: %m"); - else - x = sec * USEC_PER_SEC; /* return in µs */ + log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable, ignoring: %m"); } } else if (r < 0) @@ -2934,24 +2922,25 @@ static int property_get_reboot_to_boot_loader_entry( sd_bus_error *error) { _cleanup_free_ char *v = NULL; + Manager *m = userdata; + const char *x = NULL; int r; assert(bus); assert(reply); - assert(userdata); + assert(m); r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); if (r == -ENXIO) { /* EFI case: let's read the LoaderEntryOneShot variable */ - r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v); + r = efi_loader_update_entry_one_shot_cache(&m->efi_loader_entry_one_shot, &m->efi_loader_entry_one_shot_stat); if (r < 0) { if (r != -ENOENT) - log_warning_errno(r, "Failed to read LoaderEntryOneShot variable: %m"); - } else if (!efi_loader_entry_name_valid(v)) { - log_warning("LoaderEntryOneShot contains invalid entry name '%s', ignoring.", v); - v = mfree(v); - } + log_warning_errno(r, "Failed to read LoaderEntryOneShot variable, ignoring: %m"); + } else + x = m->efi_loader_entry_one_shot; + } else if (r < 0) log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); else if (r > 0) { @@ -2961,27 +2950,30 @@ static int property_get_reboot_to_boot_loader_entry( r = read_one_line_file("/run/systemd/reboot-to-boot-loader-entry", &v); if (r < 0) { if (r != -ENOENT) - log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry: %m"); - } else if (!efi_loader_entry_name_valid(v)) { + log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry, ignoring: %m"); + } else if (!efi_loader_entry_name_valid(v)) log_warning("/run/systemd/reboot-to-boot-loader-entry is not valid, ignoring."); - v = mfree(v); - } + else + x = v; } - return sd_bus_message_append(reply, "s", v); + return sd_bus_message_append(reply, "s", x); } -static int boot_loader_entry_exists(const char *id) { +static int boot_loader_entry_exists(Manager *m, const char *id) { _cleanup_(boot_config_free) BootConfig config = {}; int r; + assert(m); assert(id); r = boot_entries_load_config_auto(NULL, NULL, &config); if (r < 0 && r != -ENOKEY) /* don't complain if no GPT is found, hence skip ENOKEY */ return r; - (void) boot_entries_augment_from_loader(&config, true); + r = manager_read_efi_boot_loader_entries(m); + if (r >= 0) + (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true); return boot_config_has_entry(&config, id); } @@ -3006,7 +2998,7 @@ static int method_set_reboot_to_boot_loader_entry( if (isempty(v)) v = NULL; else if (efi_loader_entry_name_valid(v)) { - r = boot_loader_entry_exists(v); + r = boot_loader_entry_exists(m, v); if (r < 0) return r; if (r == 0) @@ -3125,18 +3117,21 @@ static int property_get_boot_loader_entries( sd_bus_error *error) { _cleanup_(boot_config_free) BootConfig config = {}; + Manager *m = userdata; size_t i; int r; assert(bus); assert(reply); - assert(userdata); + assert(m); r = boot_entries_load_config_auto(NULL, NULL, &config); if (r < 0 && r != -ENOKEY) /* don't complain if there's no GPT found */ return r; - (void) boot_entries_augment_from_loader(&config, true); + r = manager_read_efi_boot_loader_entries(m); + if (r >= 0) + (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) @@ -3322,7 +3317,7 @@ fail: return r; } -const sd_bus_vtable manager_vtable[] = { +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), @@ -3361,76 +3356,409 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0), SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectoryInodesMax", "t", NULL, offsetof(Manager, runtime_dir_inodes), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0), SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0), SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), - SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Halt", "b", NULL, method_halt, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SuspendThenHibernate", "b", NULL, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanSuspendThenHibernate", NULL, "s", method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootParameter", NULL, "s", method_can_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootParameter", "s", NULL, method_set_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootToBootLoaderMenu", "t", NULL, method_set_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootToBootLoaderEntry", NULL, "s", method_can_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootToBootLoaderEntry", "s", NULL, method_set_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetSession", + "s", + SD_BUS_PARAM(session_id), + "o", + SD_BUS_PARAM(object_path), + method_get_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetSessionByPID", + "u", + SD_BUS_PARAM(pid), + "o", + SD_BUS_PARAM(object_path), + method_get_session_by_pid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUser", + "u", + SD_BUS_PARAM(uid), + "o", + SD_BUS_PARAM(object_path), + method_get_user, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUserByPID", + "u", + SD_BUS_PARAM(pid), + "o", + SD_BUS_PARAM(object_path), + method_get_user_by_pid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetSeat", + "s", + SD_BUS_PARAM(seat_id), + "o", + SD_BUS_PARAM(object_path), + method_get_seat, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListSessions", + NULL,, + "a(susso)", + SD_BUS_PARAM(sessions), + method_list_sessions, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListUsers", + NULL,, + "a(uso)", + SD_BUS_PARAM(users), + method_list_users, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListSeats", + NULL,, + "a(so)", + SD_BUS_PARAM(seats), + method_list_seats, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListInhibitors", + NULL,, + "a(ssssuu)", + SD_BUS_PARAM(inhibitors), + method_list_inhibitors, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CreateSession", + "uusssssussbssa(sv)", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(pid) + SD_BUS_PARAM(service) + SD_BUS_PARAM(type) + SD_BUS_PARAM(class) + SD_BUS_PARAM(desktop) + SD_BUS_PARAM(seat_id) + SD_BUS_PARAM(vtnr) + SD_BUS_PARAM(tty) + SD_BUS_PARAM(display) + SD_BUS_PARAM(remote) + SD_BUS_PARAM(remote_user) + SD_BUS_PARAM(remote_host) + SD_BUS_PARAM(properties), + "soshusub", + SD_BUS_PARAM(session_id) + SD_BUS_PARAM(object_path) + SD_BUS_PARAM(runtime_path) + SD_BUS_PARAM(fifo_fd) + SD_BUS_PARAM(uid) + SD_BUS_PARAM(seat_id) + SD_BUS_PARAM(vtnr) + SD_BUS_PARAM(existing), + method_create_session, + 0), + SD_BUS_METHOD_WITH_NAMES("ReleaseSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_release_session, + 0), + SD_BUS_METHOD_WITH_NAMES("ActivateSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_activate_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ActivateSessionOnSeat", + "ss", + SD_BUS_PARAM(session_id) + SD_BUS_PARAM(seat_id), + NULL,, + method_activate_session_on_seat, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("LockSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_lock_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("UnlockSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_lock_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LockSessions", + NULL, + NULL, + method_lock_sessions, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnlockSessions", + NULL, + NULL, + method_lock_sessions, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("KillSession", + "ssi", + SD_BUS_PARAM(session_id) + SD_BUS_PARAM(who) + SD_BUS_PARAM(signal_number), + NULL,, + method_kill_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("KillUser", + "ui", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(signal_number), + NULL,, + method_kill_user, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TerminateSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_terminate_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TerminateUser", + "u", + SD_BUS_PARAM(uid), + NULL,, + method_terminate_user, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TerminateSeat", + "s", + SD_BUS_PARAM(seat_id), + NULL,, + method_terminate_seat, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetUserLinger", + "ubb", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(enable) + SD_BUS_PARAM(interactive), + NULL,, + method_set_user_linger, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("AttachDevice", + "ssb", + SD_BUS_PARAM(seat_id) + SD_BUS_PARAM(sysfs_path) + SD_BUS_PARAM(interactive), + NULL,, + method_attach_device, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("FlushDevices", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_flush_devices, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PowerOff", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_poweroff, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Reboot", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_reboot, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Halt", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_halt, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Suspend", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_suspend, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Hibernate", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_hibernate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("HybridSleep", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_hybrid_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SuspendThenHibernate", + "b", + SD_BUS_PARAM(interactive), + NULL,, + method_suspend_then_hibernate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanPowerOff", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_poweroff, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanReboot", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_reboot, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanHalt", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_halt, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanSuspend", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_suspend, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanHibernate", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_hibernate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanHybridSleep", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_hybrid_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanSuspendThenHibernate", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_suspend_then_hibernate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ScheduleShutdown", + "st", + SD_BUS_PARAM(type) + SD_BUS_PARAM(usec), + NULL,, + method_schedule_shutdown, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CancelScheduledShutdown", + NULL,, + "b", + SD_BUS_PARAM(cancelled), + method_cancel_scheduled_shutdown, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Inhibit", + "ssss", + SD_BUS_PARAM(what) + SD_BUS_PARAM(who) + SD_BUS_PARAM(why) + SD_BUS_PARAM(mode), + "h", + SD_BUS_PARAM(pipe_fd), + method_inhibit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanRebootParameter", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_reboot_parameter, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetRebootParameter", + "s", + SD_BUS_PARAM(parameter), + NULL,, + method_set_reboot_parameter, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanRebootToFirmwareSetup", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_reboot_to_firmware_setup, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetRebootToFirmwareSetup", + "b", + SD_BUS_PARAM(enable), + NULL,, + method_set_reboot_to_firmware_setup, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanRebootToBootLoaderMenu", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_reboot_to_boot_loader_menu, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetRebootToBootLoaderMenu", + "t", + SD_BUS_PARAM(timeout), + NULL,, + method_set_reboot_to_boot_loader_menu, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CanRebootToBootLoaderEntry", + NULL,, + "s", + SD_BUS_PARAM(result), + method_can_reboot_to_boot_loader_entry, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetRebootToBootLoaderEntry", + "s", + SD_BUS_PARAM(boot_loader_entry), + NULL,, + method_set_reboot_to_boot_loader_entry, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetWallMessage", + "sb", + SD_BUS_PARAM(wall_message) + SD_BUS_PARAM(enable), + NULL,, + method_set_wall_message, + SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("SessionNew", "so", 0), - SD_BUS_SIGNAL("SessionRemoved", "so", 0), - SD_BUS_SIGNAL("UserNew", "uo", 0), - SD_BUS_SIGNAL("UserRemoved", "uo", 0), - SD_BUS_SIGNAL("SeatNew", "so", 0), - SD_BUS_SIGNAL("SeatRemoved", "so", 0), - SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), - SD_BUS_SIGNAL("PrepareForSleep", "b", 0), + SD_BUS_SIGNAL_WITH_NAMES("SessionNew", + "so", + SD_BUS_PARAM(session_id) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("SessionRemoved", + "so", + SD_BUS_PARAM(session_id) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("UserNew", + "uo", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("UserRemoved", + "uo", + SD_BUS_PARAM(uid) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("SeatNew", + "so", + SD_BUS_PARAM(seat_id) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("SeatRemoved", + "so", + SD_BUS_PARAM(seat_id) + SD_BUS_PARAM(object_path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("PrepareForShutdown", + "b", + SD_BUS_PARAM(start), + 0), + SD_BUS_SIGNAL_WITH_NAMES("PrepareForSleep", + "b", + SD_BUS_PARAM(start), + 0), SD_BUS_VTABLE_END }; +const BusObjectImplementation manager_object = { + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + .vtables = BUS_VTABLES(manager_vtable), + .children = BUS_IMPLEMENTATIONS(&seat_object, + &session_object, + &user_object), +}; + static int session_jobs_reply(Session *s, uint32_t jid, const char *unit, const char *result) { assert(s); assert(unit); @@ -3649,13 +3977,7 @@ int manager_start_scope( assert(pid > 1); assert(job); - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(manager->bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return r; @@ -3742,11 +4064,9 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, assert(unit); assert(job); - r = sd_bus_call_method( + r = bus_call_method( manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "StartUnit", error, &reply, @@ -3765,11 +4085,9 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c assert(unit); assert(job); - r = sd_bus_call_method( + r = bus_call_method( manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "StopUnit", error, &reply, @@ -3827,11 +4145,9 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo assert(manager); assert(unit); - return sd_bus_call_method( + return bus_call_method( manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "KillUnit", error, NULL, diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h index 6c73a9654..770758754 100644 --- a/src/login/logind-dbus.h +++ b/src/login/logind-dbus.h @@ -3,9 +3,10 @@ #include "sd-bus.h" -#include "logind.h" +#include "bus-object.h" #include "logind-session.h" #include "logind-user.h" +#include "logind.h" int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret); int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret); @@ -29,3 +30,5 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error); int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error); + +extern const BusObjectImplementation manager_object; diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 983795da4..73d96ff43 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -38,6 +38,7 @@ Login.HoldoffTimeoutSec, config_parse_sec, 0, offse Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) +Login.RuntimeDirectoryInodesMax, config_parse_uint64, 0, offsetof(Manager, runtime_dir_inodes) Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 0a5df937c..a91765205 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -4,6 +4,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" #include "bus-label.h" #include "bus-polkit.h" #include "bus-util.h" @@ -17,7 +18,7 @@ #include "user-util.h" #include "util.h" -static BUS_DEFINE_PROPERTY_GET(property_get_can_multi_session, "b", Seat, seat_can_multi_session); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_const_true, "b", true); static BUS_DEFINE_PROPERTY_GET(property_get_can_tty, "b", Seat, seat_can_tty); static BUS_DEFINE_PROPERTY_GET(property_get_can_graphical, "b", Seat, seat_can_graphical); @@ -291,29 +292,7 @@ static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd return sd_bus_reply_method_return(message, NULL); } -const sd_bus_vtable seat_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { _cleanup_free_ char *e = NULL; sd_bus_message *message; Manager *m = userdata; @@ -361,7 +340,7 @@ char *seat_bus_path(Seat *s) { return strjoin("/org/freedesktop/login1/seat/", t); } -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; sd_bus_message *message; Manager *m = userdata; @@ -466,3 +445,44 @@ int seat_send_changed(Seat *s, const char *properties, ...) { return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); } + +static const sd_bus_vtable seat_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanMultiSession", "b", property_get_const_true, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD_WITH_NAMES("ActivateSession", + "s", + SD_BUS_PARAM(session_id), + NULL,, + method_activate_session, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SwitchTo", + "u", + SD_BUS_PARAM(vtnr), + NULL,, + method_switch_to, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation seat_object = { + "/org/freedesktop/login1/seat", + "org.freedesktop.login1.Seat", + .fallback_vtables = BUS_FALLBACK_VTABLES({seat_vtable, seat_object_find}), + .node_enumerator = seat_node_enumerator, +}; diff --git a/src/login/logind-seat-dbus.h b/src/login/logind-seat-dbus.h index 2590f6492..6169cfe1e 100644 --- a/src/login/logind-seat-dbus.h +++ b/src/login/logind-seat-dbus.h @@ -3,12 +3,11 @@ #include "sd-bus.h" +#include "bus-object.h" #include "logind-seat.h" -extern const sd_bus_vtable seat_vtable[]; +extern const BusObjectImplementation seat_object; -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); char *seat_bus_path(Seat *s); int seat_send_signal(Seat *s, bool new_seat); diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 394f89dd7..157fc9423 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -104,11 +104,10 @@ int seat_save(Seat *s) { fprintf(f, "# This is private data. Do not parse.\n" "IS_SEAT0=%i\n" - "CAN_MULTI_SESSION=%i\n" + "CAN_MULTI_SESSION=1\n" "CAN_TTY=%i\n" "CAN_GRAPHICAL=%i\n", seat_is_seat0(s), - seat_can_multi_session(s), seat_can_tty(s), seat_can_graphical(s)); @@ -558,12 +557,6 @@ bool seat_is_seat0(Seat *s) { return s->manager->seat0 == s; } -bool seat_can_multi_session(Seat *s) { - assert(s); - - return seat_has_vts(s); -} - bool seat_can_tty(Seat *s) { assert(s); diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h index 64cdf2f25..f4b57ce8d 100644 --- a/src/login/logind-seat.h +++ b/src/login/logind-seat.h @@ -51,7 +51,6 @@ void seat_claim_position(Seat *s, Session *session, unsigned pos); bool seat_has_vts(Seat *s); bool seat_is_seat0(Seat *s); -bool seat_can_multi_session(Seat *s); bool seat_can_tty(Seat *s); bool seat_has_master_device(Seat *s); bool seat_can_graphical(Seat *s); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 80ec89ba0..5ed20ebd2 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -4,6 +4,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" #include "bus-label.h" #include "bus-polkit.h" #include "bus-util.h" @@ -393,6 +394,32 @@ static int method_release_control(sd_bus_message *message, void *userdata, sd_bu return sd_bus_reply_method_return(message, NULL); } +static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + const char *t; + SessionType type; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "s", &t); + if (r < 0) + return r; + + type = session_type_from_string(t); + if (type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid session type '%s'", t); + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type"); + + session_set_type(s, type); + + return sd_bus_reply_method_return(message, NULL); +} + static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = userdata; uint32_t major, minor; @@ -555,57 +582,7 @@ static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus return 1; } -const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetBrightness", "ssu", NULL, method_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("PauseDevice", "uus", 0), - SD_BUS_SIGNAL("ResumeDevice", "uuh", 0), - SD_BUS_SIGNAL("Lock", NULL, 0), - SD_BUS_SIGNAL("Unlock", NULL, 0), - - SD_BUS_VTABLE_END -}; - -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { _cleanup_free_ char *e = NULL; sd_bus_message *message; Manager *m = userdata; @@ -653,7 +630,7 @@ char *session_bus_path(Session *s) { return strjoin("/org/freedesktop/login1/session/", t); } -int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; sd_bus_message *message; Manager *m = userdata; @@ -854,3 +831,144 @@ int session_send_create_reply(Session *s, sd_bus_error *error) { (uint32_t) s->vtnr, false); } + +static const sd_bus_vtable session_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", + NULL, + NULL, + bus_session_method_terminate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Activate", + NULL, + NULL, + bus_session_method_activate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Lock", + NULL, + NULL, + bus_session_method_lock, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unlock", + NULL, + NULL, + bus_session_method_lock, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetIdleHint", + "b", + SD_BUS_PARAM(idle), + NULL,, + method_set_idle_hint, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetLockedHint", + "b", + SD_BUS_PARAM(locked), + NULL,, + method_set_locked_hint, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Kill", + "si", + SD_BUS_PARAM(who) + SD_BUS_PARAM(signal_number), + NULL,, + bus_session_method_kill, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TakeControl", + "b", + SD_BUS_PARAM(force), + NULL,, + method_take_control, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseControl", + NULL, + NULL, + method_release_control, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetType", + "s", + SD_BUS_PARAM(type), + NULL,, + method_set_type, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TakeDevice", + "uu", + SD_BUS_PARAM(major) + SD_BUS_PARAM(minor), + "hb", + SD_BUS_PARAM(fd) + SD_BUS_PARAM(inactive), + method_take_device, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ReleaseDevice", + "uu", + SD_BUS_PARAM(major) + SD_BUS_PARAM(minor), + NULL,, + method_release_device, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("PauseDeviceComplete", + "uu", + SD_BUS_PARAM(major) + SD_BUS_PARAM(minor), + NULL,, + method_pause_device_complete, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetBrightness", + "ssu", + SD_BUS_PARAM(subsystem) + SD_BUS_PARAM(name) + SD_BUS_PARAM(brightness), + NULL,, + method_set_brightness, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL_WITH_NAMES("PauseDevice", + "uus", + SD_BUS_PARAM(major) + SD_BUS_PARAM(minor) + SD_BUS_PARAM(type), + 0), + SD_BUS_SIGNAL_WITH_NAMES("ResumeDevice", + "uuh", + SD_BUS_PARAM(major) + SD_BUS_PARAM(minor) + SD_BUS_PARAM(fd), + 0), + SD_BUS_SIGNAL("Lock", NULL, 0), + SD_BUS_SIGNAL("Unlock", NULL, 0), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation session_object = { + "/org/freedesktop/login1/session", + "org.freedesktop.login1.Session", + .fallback_vtables = BUS_FALLBACK_VTABLES({session_vtable, session_object_find}), + .node_enumerator = session_node_enumerator, +}; diff --git a/src/login/logind-session-dbus.h b/src/login/logind-session-dbus.h index 9d2315cc6..97f7c413c 100644 --- a/src/login/logind-session-dbus.h +++ b/src/login/logind-session-dbus.h @@ -3,11 +3,11 @@ #include "sd-bus.h" +#include "bus-object.h" #include "logind-session.h" -extern const sd_bus_vtable session_vtable[]; -int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error); -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +extern const BusObjectImplementation session_object; + char *session_bus_path(Session *s); int session_send_signal(Session *s, bool new_session); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 48505122a..5c4149e1b 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -240,6 +240,9 @@ int session_save(Session *s) { if (s->type >= 0) fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); + if (s->original_type >= 0) + fprintf(f, "ORIGINAL_TYPE=%s\n", session_type_to_string(s->original_type)); + if (s->class >= 0) fprintf(f, "CLASS=%s\n", session_class_to_string(s->class)); @@ -402,6 +405,7 @@ int session_load(Session *s) { *position = NULL, *leader = NULL, *type = NULL, + *original_type = NULL, *class = NULL, *uid = NULL, *realtime = NULL, @@ -433,6 +437,7 @@ int session_load(Session *s) { "POSITION", &position, "LEADER", &leader, "TYPE", &type, + "ORIGINAL_TYPE", &original_type, "CLASS", &class, "UID", &uid, "REALTIME", &realtime, @@ -529,6 +534,16 @@ int session_load(Session *s) { s->type = t; } + if (original_type) { + SessionType ot; + + ot = session_type_from_string(original_type); + if (ot >= 0) + s->original_type = ot; + } else + /* Pre-v246 compat: initialize original_type if not set in the state file */ + s->original_type = s->type; + if (class) { SessionClass c; @@ -1018,6 +1033,18 @@ void session_set_locked_hint(Session *s, bool b) { session_send_changed(s, "LockedHint", NULL); } +void session_set_type(Session *s, SessionType t) { + assert(s); + + if (s->type == t) + return; + + s->type = t; + session_save(s); + + session_send_changed(s, "Type", NULL); +} + static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { Session *s = userdata; @@ -1385,6 +1412,7 @@ void session_drop_controller(Session *s) { return; s->track = sd_bus_track_unref(s->track); + session_set_type(s, s->original_type); session_release_controller(s, false); session_save(s); session_restore_vt(s); diff --git a/src/login/logind-session.h b/src/login/logind-session.h index c51392bef..b87c73167 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -61,6 +61,7 @@ struct Session { const char *id; unsigned position; SessionType type; + SessionType original_type; SessionClass class; char *state_file; @@ -135,6 +136,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t); int session_set_idle_hint(Session *s, bool b); int session_get_locked_hint(Session *s); void session_set_locked_hint(Session *s, bool b); +void session_set_type(Session *s, SessionType t); int session_create_fifo(Session *s); int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error); int session_stop(Session *s, bool force); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 9bf68a610..237723729 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -3,6 +3,7 @@ #include #include "alloc-util.h" +#include "bus-get-properties.h" #include "bus-polkit.h" #include "bus-util.h" #include "format-util.h" @@ -258,31 +259,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * return sd_bus_reply_method_return(message, NULL); } -const sd_bus_vtable user_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { Manager *m = userdata; uid_t uid; User *user; @@ -337,7 +314,7 @@ char *user_bus_path(User *u) { return s; } -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; sd_bus_message *message; Manager *m = userdata; @@ -386,6 +363,42 @@ int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char *** return 1; } +static const sd_bus_vtable user_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Kill", + "i", + SD_BUS_PARAM(signal_number), + NULL,, + bus_user_method_kill, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation user_object = { + "/org/freedesktop/login1/user", + "org.freedesktop.login1.User", + .fallback_vtables = BUS_FALLBACK_VTABLES({user_vtable, user_object_find}), + .node_enumerator = user_node_enumerator, +}; + int user_send_signal(User *u, bool new_user) { _cleanup_free_ char *p = NULL; diff --git a/src/login/logind-user-dbus.h b/src/login/logind-user-dbus.h index acfcb981c..b3f990c5a 100644 --- a/src/login/logind-user-dbus.h +++ b/src/login/logind-user-dbus.h @@ -5,9 +5,8 @@ #include "logind-user.h" -extern const sd_bus_vtable user_vtable[]; -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +extern const BusObjectImplementation user_object; + char *user_bus_path(User *s); int user_send_signal(User *u, bool new_user); diff --git a/src/login/logind.c b/src/login/logind.c index caf7a169b..c50a083b0 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -10,6 +10,8 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "cgroup-util.h" #include "def.h" @@ -27,6 +29,7 @@ #include "parse-util.h" #include "process-util.h" #include "selinux-util.h" +#include "service-util.h" #include "signal-util.h" #include "strv.h" #include "terminal-util.h" @@ -165,6 +168,9 @@ static Manager* manager_unref(Manager *m) { free(m->wall_message); free(m->action_job); + strv_free(m->efi_boot_loader_entries); + free(m->efi_loader_entry_one_shot); + return mfree(m); } @@ -631,53 +637,19 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/login1", "org.freedesktop.login1.Manager", manager_vtable, m); + r = bus_add_implementation(m->bus, &manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); + return r; - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m); + r = bus_log_control_api_register(m->bus); if (r < 0) - return log_error_errno(r, "Failed to add seat object vtable: %m"); + return r; - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/seat", seat_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add seat enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/session", "org.freedesktop.login1.Session", session_vtable, session_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add session object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/session", session_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add session enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/user", "org.freedesktop.login1.User", user_vtable, user_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add user object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/user", user_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add user enumerator: %m"); - - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "JobRemoved", - match_job_removed, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to request match for JobRemoved: %m"); - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitRemoved", - match_unit_removed, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); @@ -692,26 +664,11 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reloading", - match_reloading, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m); if (r < 0) return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe", - NULL, NULL, - NULL); + r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to enable subscription: %m"); @@ -1205,16 +1162,19 @@ static int run(int argc, char *argv[]) { log_set_facility(LOG_AUTH); log_setup_service(); - umask(0022); + r = service_parse_argv("systemd-logind.service", + "Manager for user logins and devices and privileged operations.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) { - log_error("This program takes no arguments."); - return -EINVAL; - } + umask(0022); r = mac_selinux_init(); if (r < 0) - return log_error_errno(r, "Could not initialize labelling: %m"); + return r; /* Always create the directories people can create inotify watches in. Note that some applications might check * for the existence of /run/systemd/seats/ to determine whether logind is available, so please always make diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index 1029e29bc..ed1084b06 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -32,6 +32,7 @@ #IdleAction=ignore #IdleActionSec=30min #RuntimeDirectorySize=10% +#RuntimeDirectoryInodes=400k #RemoveIPC=yes #InhibitorsMax=8192 #SessionsMax=8192 diff --git a/src/login/logind.h b/src/login/logind.h index 7e4673d27..e64ecce8e 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -120,8 +120,15 @@ struct Manager { sd_event_source *lid_switch_ignore_event_source; uint64_t runtime_dir_size; + uint64_t runtime_dir_inodes; uint64_t sessions_max; uint64_t inhibitors_max; + + char **efi_boot_loader_entries; + bool efi_boot_loader_entries_set; + + char *efi_loader_entry_one_shot; + struct stat efi_loader_entry_one_shot_stat; }; void manager_reset_config(Manager *m); @@ -157,8 +164,6 @@ int manager_read_utmp(Manager *m); void manager_connect_utmp(Manager *m); void manager_reconnect_utmp(Manager *m); -extern const sd_bus_vtable manager_vtable[]; - /* gperf lookup function */ const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length); @@ -169,3 +174,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs_size); int manager_setup_wall_message_timer(Manager *m); bool logind_wall_tty_filter(const char *tty, void *userdata); + +int manager_read_efi_boot_loader_entries(Manager *m); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index 124a25810..df46e417c 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -290,6 +290,10 @@ send_interface="org.freedesktop.login1.Session" send_member="ReleaseControl"/> + + diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 51d0610bf..16f428958 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -20,7 +20,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-internal.h" -#include "bus-util.h" +#include "bus-locator.h" #include "cgroup-setup.h" #include "errno-util.h" #include "fd-util.h" @@ -99,6 +99,7 @@ static int acquire_user_record( _cleanup_(user_record_unrefp) UserRecord *ur = NULL; const char *username = NULL, *json = NULL; + _cleanup_free_ char *field = NULL; int r; assert(handle); @@ -114,39 +115,18 @@ static int acquire_user_record( return PAM_SERVICE_ERR; } - /* If pam_systemd_homed (or some other module) already acqired the user record we can reuse it + /* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it * here. */ - r = pam_get_data(handle, "systemd-user-record", (const void**) &json); - if (r != PAM_SUCCESS || !json) { - _cleanup_free_ char *formatted = NULL; + field = strjoin("systemd-user-record-", username); + if (!field) + return pam_log_oom(handle); - if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { - pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r)); - return r; - } - - /* Request the record ourselves */ - r = userdb_by_name(username, 0, &ur); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r)); - return PAM_USER_UNKNOWN; - } - - r = json_variant_format(ur->json, 0, &formatted); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r)); - return PAM_SERVICE_ERR; - } - - /* And cache it for everyone else */ - r = pam_set_data(handle, "systemd-user-record", formatted, pam_cleanup_free); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r)); - return r; - } - - TAKE_PTR(formatted); - } else { + r = pam_get_data(handle, field, (const void**) &json); + if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) { + pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r)); + return r; + } + if (r == PAM_SUCCESS && json) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; /* Parse cached record */ @@ -171,6 +151,31 @@ static int acquire_user_record( pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name."); return PAM_SERVICE_ERR; } + } else { + _cleanup_free_ char *formatted = NULL; + + /* Request the record ourselves */ + r = userdb_by_name(username, 0, &ur); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r)); + return PAM_USER_UNKNOWN; + } + + r = json_variant_format(ur->json, 0, &formatted); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r)); + return PAM_SERVICE_ERR; + } + + /* And cache it for everyone else */ + r = pam_set_data(handle, field, formatted, pam_cleanup_free); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s", + field, pam_strerror(handle, r)); + return r; + } + + TAKE_PTR(formatted); } if (!uid_is_valid(ur->uid)) { @@ -280,7 +285,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ static int export_legacy_dbus_address( pam_handle_t *handle, - uid_t uid, const char *runtime) { const char *s; @@ -461,8 +465,17 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u assert(handle); assert(path); - /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set - * up properly for us. */ + /* Some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually + * set up properly for us. This is supposed to provide a careful safety net for supporting su/sudo + * type transitions: in that case the UID changes, but the session and thus the user owning it + * doesn't change. Since the $XDG_RUNTIME_DIR lifecycle is bound to the session's user being logged + * in at least once we should be particularly careful when setting the environment variable, since + * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */ + + if (!path_is_absolute(path)) { + pam_syslog(handle, LOG_ERR, "Provided runtime directory '%s' is not absolute.", path); + goto fail; + } if (lstat(path, &st) < 0) { pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno)); @@ -580,9 +593,9 @@ static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool if (pam_getenv(handle, "LANG")) { if (debug) pam_syslog(handle, LOG_DEBUG, "PAM environment variable $LANG already set, not changing based on user record."); - } else if (!locale_is_valid(ur->preferred_language)) { + } else if (locale_is_installed(ur->preferred_language) <= 0) { if (debug) - pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid locally, not setting $LANG."); + pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid or not installed, not setting $LANG."); } else { _cleanup_free_ char *joined = NULL; @@ -618,6 +631,29 @@ static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool return PAM_SUCCESS; } +static int configure_runtime_directory( + pam_handle_t *handle, + UserRecord *ur, + const char *rt) { + + int r; + + assert(handle); + assert(ur); + assert(rt); + + if (!validate_runtime_directory(handle, rt, ur->uid)) + return PAM_SUCCESS; + + r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r)); + return r; + } + + return export_legacy_dbus_address(handle, rt); +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, @@ -643,10 +679,6 @@ _public_ PAM_EXTERN int pam_sm_open_session( assert(handle); - /* Make this a NOP on non-logind systems */ - if (!logind_running()) - return PAM_SUCCESS; - if (parse_argv(handle, argc, argv, &class_pam, @@ -662,6 +694,10 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (r != PAM_SUCCESS) return r; + /* Make most of this a NOP on non-logind systems */ + if (!logind_running()) + goto success; + /* Make sure we don't enter a loop by talking to * systemd-logind when it is actually waiting for the * background to finish start-up. If the service is @@ -673,23 +709,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)]; xsprintf(rt, "/run/user/"UID_FMT, ur->uid); - if (validate_runtime_directory(handle, rt, ur->uid)) { - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r)); - return r; - } - } - - r = export_legacy_dbus_address(handle, ur->uid, rt); + r = configure_runtime_directory(handle, ur, rt); if (r != PAM_SUCCESS) return r; - r = apply_user_record_settings(handle, ur, debug); - if (r != PAM_SUCCESS) - return r; - - return PAM_SUCCESS; + goto success; } /* Otherwise, we ask logind to create a session for us */ @@ -789,13 +813,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec)); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CreateSession"); + r = bus_message_new_method_call(bus, &m, bus_login_mgr, "CreateSession"); if (r < 0) return pam_bus_log_create_error(handle, r); @@ -849,7 +867,9 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) { if (debug) pam_syslog(handle, LOG_DEBUG, "Not creating session: %s", bus_error_message(&error, r)); - return PAM_SUCCESS; + + /* We are already in a session, don't do anything */ + goto success; } else { pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); return PAM_SESSION_ERR; @@ -879,19 +899,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( return r; if (original_uid == ur->uid) { - /* Don't set $XDG_RUNTIME_DIR if the user we now - * authenticated for does not match the original user - * of the session. We do this in order not to result - * in privileged apps clobbering the runtime directory - * unnecessarily. */ + /* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the + * original user of the session. We do this in order not to result in privileged apps + * clobbering the runtime directory unnecessarily. */ - if (validate_runtime_directory(handle, runtime_path, ur->uid)) { - r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path); - if (r != PAM_SUCCESS) - return r; - } - - r = export_legacy_dbus_address(handle, ur->uid, runtime_path); + r = configure_runtime_directory(handle, ur, runtime_path); if (r != PAM_SUCCESS) return r; } @@ -946,6 +958,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( } } +success: r = apply_user_record_settings(handle, ur, debug); if (r != PAM_SUCCESS) return r; @@ -996,15 +1009,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (r != PAM_SUCCESS) return r; - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ReleaseSession", - &error, - NULL, - "s", - id); + r = bus_call_method(bus, bus_login_mgr, "ReleaseSession", &error, NULL, "s", id); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r)); return PAM_SESSION_ERR; diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index 1f98898b6..38058d7b2 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -22,7 +22,7 @@ #include "strv.h" #include "user-util.h" -static int acquire_runtime_dir_size(uint64_t *ret) { +static int acquire_runtime_dir_properties(uint64_t *size, uint64_t *inodes) { _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; @@ -31,10 +31,14 @@ static int acquire_runtime_dir_size(uint64_t *ret) { if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); - r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret); + r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', size); if (r < 0) return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r)); + r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectoryInodesMax", &error, 't', inodes); + if (r < 0) + return log_error_errno(r, "Failed to acquire number of inodes for runtime directory: %s", bus_error_message(&error, r)); + return 0; } @@ -42,7 +46,8 @@ static int user_mkdir_runtime_path( const char *runtime_path, uid_t uid, gid_t gid, - uint64_t runtime_dir_size) { + uint64_t runtime_dir_size, + uint64_t runtime_dir_inodes) { int r; @@ -58,21 +63,22 @@ static int user_mkdir_runtime_path( if (path_is_mount_point(runtime_path, NULL, 0) >= 0) log_debug("%s is already a mount point", runtime_path); else { - char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*") + char options[sizeof("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*") + DECIMAL_STR_MAX(uid_t) + DECIMAL_STR_MAX(gid_t) + + DECIMAL_STR_MAX(uint64_t) + DECIMAL_STR_MAX(uint64_t)]; xsprintf(options, - "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s", - uid, gid, runtime_dir_size, + "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 ",nr_inodes=%" PRIu64 "%s", + uid, gid, runtime_dir_size, runtime_dir_inodes, mac_smack_use() ? ",smackfsroot=*" : ""); (void) mkdir_label(runtime_path, 0700); r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options); if (r < 0) { - if (!IN_SET(errno, EPERM, EACCES)) { + if (!ERRNO_IS_PRIVILEGE(errno)) { r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path); goto fail; } @@ -127,7 +133,7 @@ static int user_remove_runtime_path(const char *runtime_path) { static int do_mount(const char *user) { char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)]; - uint64_t runtime_dir_size; + uint64_t runtime_dir_size, runtime_dir_inodes; uid_t uid; gid_t gid; int r; @@ -140,14 +146,14 @@ static int do_mount(const char *user) { : "Failed to look up user \"%s\": %m", user); - r = acquire_runtime_dir_size(&runtime_dir_size); + r = acquire_runtime_dir_properties(&runtime_dir_size, &runtime_dir_inodes); if (r < 0) return r; xsprintf(runtime_path, "/run/user/" UID_FMT, uid); log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid); - return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size); + return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size, runtime_dir_inodes); } static int do_umount(const char *user) { @@ -186,11 +192,11 @@ static int run(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "First argument must be either \"start\" or \"stop\"."); + umask(0022); + r = mac_selinux_init(); if (r < 0) - return log_error_errno(r, "Could not initialize labelling: %m\n"); - - umask(0022); + return r; if (streq(argv[1], "start")) return do_mount(argv[2]); diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 294ef3493..7a15bcc49 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -4,9 +4,9 @@ #include #include "alloc-util.h" +#include "bus-get-properties.h" #include "bus-label.h" #include "bus-polkit.h" -#include "bus-util.h" #include "copy.h" #include "dissect-image.h" #include "fd-util.h" @@ -354,30 +354,6 @@ int bus_image_method_get_os_release( return bus_reply_pair_array(message, image->os_release); } -const sd_bus_vtable image_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0), - SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0), - SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0), - SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0), - SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0), - SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0), - SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0), - SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), - SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), - SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - static int image_flush_cache(sd_event_source *s, void *userdata) { Manager *m = userdata; @@ -388,7 +364,7 @@ static int image_flush_cache(sd_event_source *s, void *userdata) { return 0; } -int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { _cleanup_free_ char *e = NULL; Manager *m = userdata; Image *image = NULL; @@ -462,7 +438,7 @@ char *image_bus_path(const char *name) { return strjoin("/org/freedesktop/machine1/image/", e); } -int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_hashmap_free_ Hashmap *images = NULL; _cleanup_strv_free_ char **l = NULL; Image *image; @@ -497,3 +473,34 @@ int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ** return 1; } + +const sd_bus_vtable image_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0), + SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0), + SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0), + SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0), + SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0), + SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0), + SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0), + SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), + SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), + SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation image_object = { + "/org/freedesktop/machine1/image", + "org.freedesktop.machine1.Image", + .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, image_object_find}), + .node_enumerator = image_node_enumerator, +}; diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h index a918b77d3..d785c1081 100644 --- a/src/machine/image-dbus.h +++ b/src/machine/image-dbus.h @@ -1,15 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "bus-object.h" #include "machined.h" -extern const sd_bus_vtable image_vtable[]; +extern const BusObjectImplementation image_object; char *image_bus_path(const char *name); -int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); - int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index a2990452a..73ef5949b 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -12,14 +12,16 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" #include "bus-internal.h" #include "bus-label.h" +#include "bus-locator.h" #include "bus-polkit.h" -#include "bus-util.h" #include "copy.h" #include "env-file.h" #include "env-util.h" #include "fd-util.h" +#include "fileio.h" #include "format-util.h" #include "fs-util.h" #include "in-addr-util.h" @@ -399,12 +401,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s pair[1] = safe_close(pair[1]); - f = fdopen(pair[0], "r"); + f = take_fdopen(&pair[0], "r"); if (!f) return -errno; - pair[0] = -1; - r = load_env_file_pairs(f, "/etc/os-release", &l); if (r < 0) return r; @@ -553,14 +553,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu getty = strjoina("container-getty@", p, ".service"); - r = sd_bus_call_method( - container_bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, NULL, - "ss", getty, "replace"); + r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, NULL, "ss", getty, "replace"); if (r < 0) return r; @@ -669,13 +662,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu container_bus = allocated_bus ?: m->manager->bus; - r = sd_bus_message_new_method_call( - container_bus, - &tm, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return r; @@ -1322,35 +1309,7 @@ int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd return sd_bus_reply_method_return(message, "u", (uint32_t) shift); } -const sd_bus_vtable machine_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { Manager *m = userdata; Machine *machine; int r; @@ -1414,7 +1373,7 @@ char *machine_bus_path(Machine *m) { return strjoin("/org/freedesktop/machine1/machine/", e); } -int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; Machine *machine = NULL; Manager *m = userdata; @@ -1442,6 +1401,115 @@ int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char return 1; } +static const sd_bus_vtable machine_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + + SD_BUS_METHOD("Terminate", + NULL, + NULL, + bus_machine_method_terminate, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("Kill", + "si", + SD_BUS_PARAM(who) + SD_BUS_PARAM(signal), + NULL,, + bus_machine_method_kill, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetAddresses", + NULL,, + "a(iay)", + SD_BUS_PARAM(addresses), + bus_machine_method_get_addresses, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetOSRelease", + NULL,, + "a{ss}", + SD_BUS_PARAM(fields), + bus_machine_method_get_os_release, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetUIDShift", + NULL,, + "u", + SD_BUS_PARAM(shift), + bus_machine_method_get_uid_shift, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenPTY", + NULL,, + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + bus_machine_method_open_pty, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenLogin", + NULL,, + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + bus_machine_method_open_login, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenShell", + "ssasas", + SD_BUS_PARAM(user) + SD_BUS_PARAM(path) + SD_BUS_PARAM(args) + SD_BUS_PARAM(environment), + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + bus_machine_method_open_shell, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("BindMount", + "ssbb", + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination) + SD_BUS_PARAM(read_only) + SD_BUS_PARAM(mkdir), + NULL,, + bus_machine_method_bind_mount, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CopyFrom", + "ss", + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination), + NULL,, + bus_machine_method_copy, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CopyTo", + "ss", + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination), + NULL,, + bus_machine_method_copy, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenRootDirectory", + NULL,, + "h", + SD_BUS_PARAM(fd), + bus_machine_method_open_root_directory, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation machine_object = { + "/org/freedesktop/machine1/machine", + "org.freedesktop.machine1.Machine", + .fallback_vtables = BUS_FALLBACK_VTABLES({machine_vtable, machine_object_find}), + .node_enumerator = machine_node_enumerator, +}; + int machine_send_signal(Machine *m, bool new_machine) { _cleanup_free_ char *p = NULL; diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d9f3c59ce..7080092bc 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -3,13 +3,12 @@ #include "sd-bus.h" +#include "bus-util.h" #include "machine.h" -extern const sd_bus_vtable machine_vtable[]; +extern const BusObjectImplementation machine_object; char *machine_bus_path(Machine *s); -int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index 8154c42ed..ace84edbb 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -746,6 +746,143 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { return 0; } +static int machine_owns_uid_internal( + Machine *machine, + const char *map_file, /* "uid_map" or "gid_map" */ + uid_t uid, + uid_t *ret_internal_uid) { + + _cleanup_fclose_ FILE *f = NULL; + const char *p; + + /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + assert(machine); + + /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to + * internally in the machine */ + + if (machine->class != MACHINE_CONTAINER) + goto negative; + + p = procfs_file_alloca(machine->leader, map_file); + f = fopen(p, "re"); + if (!f) { + log_debug_errno(errno, "Failed to open %s, ignoring.", p); + goto negative; + } + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f)) + return errno_or_else(EIO); + + return -EIO; + } + + /* The private user namespace is disabled, ignoring. */ + if (uid_shift == 0) + continue; + + if (uid < uid_shift || uid >= uid_shift + uid_range) + continue; + + converted = (uid - uid_shift + uid_base); + if (!uid_is_valid(converted)) + return -EINVAL; + + if (ret_internal_uid) + *ret_internal_uid = converted; + + return true; + } + +negative: + if (ret_internal_uid) + *ret_internal_uid = UID_INVALID; + + return false; +} + +int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) { + return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid); +} + +int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) { + return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid); +} + +static int machine_translate_uid_internal( + Machine *machine, + const char *map_file, /* "uid_map" or "gid_map" */ + uid_t uid, + uid_t *ret_host_uid) { + + _cleanup_fclose_ FILE *f = NULL; + const char *p; + + /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + assert(machine); + assert(uid_is_valid(uid)); + + if (machine->class != MACHINE_CONTAINER) + return -ESRCH; + + /* Translates a machine UID into a host UID */ + + p = procfs_file_alloca(machine->leader, map_file); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f)) + return errno_or_else(EIO); + + return -EIO; + } + + if (uid < uid_base || uid >= uid_base + uid_range) + continue; + + converted = uid - uid_base + uid_shift; + if (!uid_is_valid(converted)) + return -EINVAL; + + if (ret_host_uid) + *ret_host_uid = converted; + return 0; + } + + return -ESRCH; +} + +int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) { + return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid); +} + +int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) { + return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid); +} + static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", [MACHINE_VM] = "vm", diff --git a/src/machine/machine.h b/src/machine/machine.h index 0a39e6105..634c5fc64 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -94,3 +94,9 @@ int machine_openpt(Machine *m, int flags, char **ret_slave); int machine_open_terminal(Machine *m, const char *path, int mode); int machine_get_uid_shift(Machine *m, uid_t *ret); + +int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid); +int machine_owns_gid(Machine *m, gid_t host_gid, gid_t *ret_internal_gid); + +int machine_translate_uid(Machine *m, uid_t internal_uid, uid_t *ret_host_uid); +int machine_translate_gid(Machine *m, gid_t internal_gid, gid_t *ret_host_gid); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 61234582a..841eeae88 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -16,9 +16,11 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-print-properties.h" #include "bus-unit-procs.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "bus-wait-for-jobs.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -108,14 +110,7 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name awaited_args++; query_res = newa0(const char *, awaited_args); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - method, - &error, - &reply, "s", name); + r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name); if (r < 0) return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r)); @@ -179,14 +174,7 @@ static int call_get_addresses( assert(prefix); assert(prefix2); - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name); if (r < 0) return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r)); @@ -249,7 +237,7 @@ static int show_table(Table *table, const char *word) { if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) - return log_error_errno(r, "Failed to sort table: %m"); + return table_log_sort_error(r); table_set_header(table, arg_legend); @@ -258,7 +246,7 @@ static int show_table(Table *table, const char *word) { else r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } if (arg_legend) { @@ -283,14 +271,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListMachines", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_machine_mgr, "ListMachines", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r)); @@ -369,14 +350,7 @@ static int list_images(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListImages", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_machine_mgr, "ListImages", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r)); @@ -493,14 +467,7 @@ static int print_uid_shift(sd_bus *bus, const char *name) { assert(bus); assert(name); - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineUIDShift", - &error, - &reply, - "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetMachineUIDShift", &error, &reply, "s", name); if (r < 0) return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r)); @@ -751,14 +718,7 @@ static int show_machine(int argc, char *argv[], void *userdata) { for (int i = 1; i < argc; i++) { const char *path = NULL; - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachine", - &error, - &reply, - "s", argv[i]); + r = bus_call_method(bus, bus_machine_mgr, "GetMachine", &error, &reply, "s", argv[i]); if (r < 0) return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, -r)); @@ -780,13 +740,7 @@ static int print_image_hostname(sd_bus *bus, const char *name) { const char *hn; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImageHostname", - NULL, &reply, "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetImageHostname", NULL, &reply, "s", name); if (r < 0) return r; @@ -807,13 +761,7 @@ static int print_image_machine_id(sd_bus *bus, const char *name) { size_t size; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImageMachineID", - NULL, &reply, "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name); if (r < 0) return r; @@ -834,13 +782,7 @@ static int print_image_machine_info(sd_bus *bus, const char *name) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImageMachineInfo", - NULL, &reply, "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineInfo", NULL, &reply, "s", name); if (r < 0) return r; @@ -1093,15 +1035,7 @@ static int show_image(int argc, char *argv[], void *userdata) { for (int i = 1; i < argc; i++) { const char *path = NULL; - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImage", - &error, - &reply, - "s", argv[i]); + r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, &reply, "s", argv[i]); if (r < 0) return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, -r)); @@ -1131,11 +1065,9 @@ static int kill_machine(int argc, char *argv[], void *userdata) { arg_kill_who = "all"; for (int i = 1; i < argc; i++) { - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", + bus_machine_mgr, "KillMachine", &error, NULL, @@ -1171,15 +1103,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); for (int i = 1; i < argc; i++) { - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "TerminateMachine", - &error, - NULL, - "s", argv[i]); + r = bus_call_method(bus, bus_machine_mgr, "TerminateMachine", &error, NULL, "s", argv[i]); if (r < 0) return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, -r)); } @@ -1213,12 +1137,10 @@ static int copy_files(int argc, char *argv[], void *userdata) { host_path = abs_host_path; } - r = sd_bus_message_new_method_call( + r = bus_message_new_method_call( bus, &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", + bus_machine_mgr, copy_from ? "CopyFromMachine" : "CopyToMachine"); if (r < 0) return bus_log_create_error(r); @@ -1249,11 +1171,9 @@ static int bind_mount(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", + bus_machine_mgr, "BindMountMachine", &error, NULL, @@ -1425,15 +1345,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to request machine removal match: %m"); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachineLogin", - &error, - &reply, - "s", machine); + r = bus_call_method(bus, bus_machine_mgr, "OpenMachineLogin", &error, &reply, "s", machine); if (r < 0) return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, -r)); @@ -1498,13 +1410,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to request machine removal match: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachineShell"); + r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "OpenMachineShell"); if (r < 0) return bus_log_create_error(r); @@ -1545,13 +1451,7 @@ static int remove_image(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "RemoveImage"); + r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RemoveImage"); if (r < 0) return bus_log_create_error(r); @@ -1577,11 +1477,9 @@ static int rename_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", + bus_machine_mgr, "RenameImage", &error, NULL, @@ -1602,13 +1500,7 @@ static int clone_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CloneImage"); + r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CloneImage"); if (r < 0) return bus_log_create_error(r); @@ -1641,15 +1533,7 @@ static int read_only_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MarkImageReadOnly", - &error, - NULL, - "sb", argv[1], b); + r = bus_call_method(bus, bus_machine_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b); if (r < 0) return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, -r)); @@ -1663,15 +1547,7 @@ static int image_exists(sd_bus *bus, const char *name) { assert(bus); assert(name); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImage", - &error, - NULL, - "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, NULL, "s", name); if (r < 0) { if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE)) return 0; @@ -1933,12 +1809,10 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - r = sd_bus_match_signal_async( + r = bus_match_signal_async( bus, &slot_job_removed, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", + bus_import_mgr, "TransferRemoved", match_transfer_removed, NULL, &path); if (r < 0) @@ -2022,13 +1896,7 @@ static int import_tar(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open %s: %m", path); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ImportTar"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTar"); if (r < 0) return bus_log_create_error(r); @@ -2089,13 +1957,7 @@ static int import_raw(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open %s: %m", path); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ImportRaw"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRaw"); if (r < 0) return bus_log_create_error(r); @@ -2150,13 +2012,7 @@ static int import_fs(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open directory '%s': %m", path); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ImportFileSystem"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystem"); if (r < 0) return bus_log_create_error(r); @@ -2215,13 +2071,7 @@ static int export_tar(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open %s: %m", path); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ExportTar"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTar"); if (r < 0) return bus_log_create_error(r); @@ -2264,13 +2114,7 @@ static int export_raw(int argc, char *argv[], void *userdata) { return log_error_errno(errno, "Failed to open %s: %m", path); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ExportRaw"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRaw"); if (r < 0) return bus_log_create_error(r); @@ -2326,13 +2170,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { } } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "PullTar"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar"); if (r < 0) return bus_log_create_error(r); @@ -2389,13 +2227,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { } } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "PullRaw"); + r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw"); if (r < 0) return bus_log_create_error(r); @@ -2438,14 +2270,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_call_method(bus, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ListTransfers", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_import_mgr, "ListTransfers", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, -r)); @@ -2542,15 +2367,7 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]); - r = sd_bus_call_method( - bus, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "CancelTransfer", - &error, - NULL, - "u", id); + r = bus_call_method(bus, bus_import_mgr, "CancelTransfer", &error, NULL, "u", id); if (r < 0) return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, -r)); } @@ -2577,26 +2394,10 @@ static int set_limit(int argc, char *argv[], void *userdata) { if (argc > 2) /* With two arguments changes the quota limit of the * specified image */ - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "SetImageLimit", - &error, - NULL, - "st", argv[1], limit); + r = bus_call_method(bus, bus_machine_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit); else /* With one argument changes the pool quota limit */ - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "SetPoolLimit", - &error, - NULL, - "t", limit); + r = bus_call_method(bus, bus_machine_mgr, "SetPoolLimit", &error, NULL, "t", limit); if (r < 0) return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r)); @@ -2616,13 +2417,7 @@ static int clean_images(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CleanPool"); + r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CleanPool"); if (r < 0) return bus_log_create_error(r); @@ -3089,9 +2884,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); /* The journal merging logic potentially needs a lot of fds. */ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); @@ -3104,7 +2897,7 @@ static int run(int argc, char *argv[]) { r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c index 6a404805e..c44bb94d8 100644 --- a/src/machine/machined-core.c +++ b/src/machine/machined-core.c @@ -3,6 +3,7 @@ #include "machined.h" #include "nscd-flush.h" #include "strv.h" +#include "user-util.h" static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) { /* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show @@ -34,3 +35,72 @@ int manager_enqueue_nscd_cache_flush(Manager *m) { return 0; } + +int manager_find_machine_for_uid(Manager *m, uid_t uid, Machine **ret_machine, uid_t *ret_internal_uid) { + Machine *machine; + Iterator i; + int r; + + assert(m); + assert(uid_is_valid(uid)); + + /* Finds the machine for the specified host UID and returns it along with the UID translated into the + * internal UID inside the machine */ + + HASHMAP_FOREACH(machine, m->machines, i) { + uid_t converted; + + r = machine_owns_uid(machine, uid, &converted); + if (r < 0) + return r; + if (r) { + if (ret_machine) + *ret_machine = machine; + + if (ret_internal_uid) + *ret_internal_uid = converted; + + return true; + } + } + + if (ret_machine) + *ret_machine = NULL; + if (ret_internal_uid) + *ret_internal_uid = UID_INVALID; + + return false; +} + +int manager_find_machine_for_gid(Manager *m, gid_t gid, Machine **ret_machine, gid_t *ret_internal_gid) { + Machine *machine; + Iterator i; + int r; + + assert(m); + assert(gid_is_valid(gid)); + + HASHMAP_FOREACH(machine, m->machines, i) { + gid_t converted; + + r = machine_owns_gid(machine, gid, &converted); + if (r < 0) + return r; + if (r) { + if (ret_machine) + *ret_machine = machine; + + if (ret_internal_gid) + *ret_internal_gid = converted; + + return true; + } + } + + if (ret_machine) + *ret_machine = NULL; + if (ret_internal_gid) + *ret_internal_gid = GID_INVALID; + + return false; +} diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 07b090507..467f16b72 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -8,8 +8,9 @@ #include "alloc-util.h" #include "btrfs-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" +#include "bus-locator.h" #include "bus-polkit.h" -#include "bus-util.h" #include "cgroup-util.h" #include "errno-util.h" #include "fd-util.h" @@ -620,12 +621,10 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1) return -errno; - f = fdopen(operation->extra_fd, "r"); + f = take_fdopen(&operation->extra_fd, "r"); if (!f) return -errno; - operation->extra_fd = -1; - /* The resulting temporary file starts with a boolean value that indicates success or not. */ errno = 0; n = fread(&success, 1, sizeof(success), f); @@ -884,11 +883,11 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu } static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; Manager *m = userdata; - const char *name, *p; + const char *name; Machine *machine; uint32_t uid; + uid_t converted; int r; r = sd_bus_message_read(message, "su", &name, &uid); @@ -905,44 +904,20 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (machine->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - p = procfs_file_alloca(machine->leader, "uid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; + r = machine_translate_uid(machine, uid, &converted); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); + if (r < 0) + return r; - for (;;) { - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - if (uid < uid_base || uid >= uid_base + uid_range) - continue; - - converted = uid - uid_base + uid_shift; - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); } static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *o = NULL; Manager *m = userdata; Machine *machine; - uid_t uid; - Iterator i; + uid_t uid, converted; int r; r = sd_bus_message_read(message, "u", &uid); @@ -953,63 +928,24 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s if (uid < 0x10000) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid); - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + r = manager_find_machine_for_uid(m, uid, &machine, &converted); + if (r < 0) + return r; + if (!r) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); - if (machine->class != MACHINE_CONTAINER) - continue; + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; - xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed to open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - /* The private user namespace is disabled, ignoring. */ - if (uid_shift == 0) - continue; - - if (uid < uid_shift || uid >= uid_shift + uid_range) - continue; - - converted = (uid - uid_shift + uid_base); - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); } -static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; - Manager *m = groupdata; - const char *name, *p; +static int method_map_from_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; Machine *machine; + gid_t converted; uint32_t gid; int r; @@ -1027,44 +963,20 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (machine->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - p = procfs_file_alloca(machine->leader, "gid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; + r = machine_translate_gid(machine, gid, &converted); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching group mappings.", name); + if (r < 0) + return r; - for (;;) { - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - if (gid < gid_base || gid >= gid_base + gid_range) - continue; - - converted = gid - gid_base + gid_shift; - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name); + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); } -static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - Manager *m = groupdata; +static int method_map_to_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *o = NULL; + Manager *m = userdata; Machine *machine; - gid_t gid; - Iterator i; + gid_t gid, converted; int r; r = sd_bus_message_read(message, "u", &gid); @@ -1075,105 +987,342 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, if (gid < 0x10000) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid); - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[STRLEN("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + r = manager_find_machine_for_gid(m, gid, &machine, &converted); + if (r < 0) + return r; + if (!r) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); - if (machine->class != MACHINE_CONTAINER) - continue; + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; - xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed to open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - /* The private user namespace is disabled, ignoring. */ - if (gid_shift == 0) - continue; - - if (gid < gid_shift || gid >= gid_shift + gid_range) - continue; - - converted = (gid - gid_shift + gid_base); - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); } const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0), SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0), SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0), - SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0), - SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0), - SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), - SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0), - SD_BUS_METHOD("UnregisterMachine", "s", NULL, method_unregister_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), - SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImageHostname", "s", "s", method_get_image_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImageMachineID", "s", "ay", method_get_image_machine_id, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImageMachineInfo", "s", "a{ss}", method_get_image_machine_info, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("MachineNew", "so", 0), - SD_BUS_SIGNAL("MachineRemoved", "so", 0), + + SD_BUS_METHOD_WITH_NAMES("GetMachine", + "s", + SD_BUS_PARAM(name), + "o", + SD_BUS_PARAM(machine), + method_get_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetImage", + "s", + SD_BUS_PARAM(name), + "o", + SD_BUS_PARAM(image), + method_get_image, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetMachineByPID", + "u", + SD_BUS_PARAM(pid), + "o", + SD_BUS_PARAM(machine), + method_get_machine_by_pid, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListMachines", + NULL,, + "a(ssso)", + SD_BUS_PARAM(machines), + method_list_machines, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListImages", + NULL,, + "a(ssbttto)", + SD_BUS_PARAM(images), + method_list_images, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CreateMachine", + "sayssusa(sv)", + SD_BUS_PARAM(name) + SD_BUS_PARAM(id) + SD_BUS_PARAM(service) + SD_BUS_PARAM(class) + SD_BUS_PARAM(leader) + SD_BUS_PARAM(root_directory) + SD_BUS_PARAM(scope_properties), + "o", + SD_BUS_PARAM(path), + method_create_machine, 0), + SD_BUS_METHOD_WITH_NAMES("CreateMachineWithNetwork", + "sayssusaia(sv)", + SD_BUS_PARAM(name) + SD_BUS_PARAM(id) + SD_BUS_PARAM(service) + SD_BUS_PARAM(class) + SD_BUS_PARAM(leader) + SD_BUS_PARAM(root_directory) + SD_BUS_PARAM(ifindices) + SD_BUS_PARAM(scope_properties), + "o", + SD_BUS_PARAM(path), + method_create_machine_with_network, 0), + SD_BUS_METHOD_WITH_NAMES("RegisterMachine", + "sayssus", + SD_BUS_PARAM(name) + SD_BUS_PARAM(id) + SD_BUS_PARAM(service) + SD_BUS_PARAM(class) + SD_BUS_PARAM(leader) + SD_BUS_PARAM(root_directory), + "o", + SD_BUS_PARAM(path), + method_register_machine, 0), + SD_BUS_METHOD_WITH_NAMES("RegisterMachineWithNetwork", + "sayssusai", + SD_BUS_PARAM(name) + SD_BUS_PARAM(id) + SD_BUS_PARAM(service) + SD_BUS_PARAM(class) + SD_BUS_PARAM(leader) + SD_BUS_PARAM(root_directory) + SD_BUS_PARAM(ifindices), + "o", + SD_BUS_PARAM(path), + method_register_machine_with_network, 0), + SD_BUS_METHOD_WITH_NAMES("UnregisterMachine", + "s", + SD_BUS_PARAM(name), + NULL,, + method_unregister_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("TerminateMachine", + "s", + SD_BUS_PARAM(id), + NULL,, + method_terminate_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("KillMachine", + "ssi", + SD_BUS_PARAM(name) + SD_BUS_PARAM(who) + SD_BUS_PARAM(signal), + NULL,, + method_kill_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetMachineAddresses", + "s", + SD_BUS_PARAM(name), + "a(iay)", + SD_BUS_PARAM(addresses), + method_get_machine_addresses, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetMachineOSRelease", + "s", + SD_BUS_PARAM(name), + "a{ss}", + SD_BUS_PARAM(fields), + method_get_machine_os_release, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenMachinePTY", + "s", + SD_BUS_PARAM(name), + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + method_open_machine_pty, + 0), + SD_BUS_METHOD_WITH_NAMES("OpenMachineLogin", + "s", + SD_BUS_PARAM(name), + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + method_open_machine_login, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenMachineShell", + "sssasas", + SD_BUS_PARAM(name) + SD_BUS_PARAM(user) + SD_BUS_PARAM(path) + SD_BUS_PARAM(args) + SD_BUS_PARAM(environment), + "hs", + SD_BUS_PARAM(pty) + SD_BUS_PARAM(pty_path), + method_open_machine_shell, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("BindMountMachine", + "sssbb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination) + SD_BUS_PARAM(read_only) + SD_BUS_PARAM(mkdir), + NULL,, + method_bind_mount_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CopyFromMachine", + "sss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination), + NULL,, + method_copy_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CopyToMachine", + "sss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(source) + SD_BUS_PARAM(destination), + NULL,, + method_copy_machine, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("OpenMachineRootDirectory", + "s", + SD_BUS_PARAM(name), + "h", + SD_BUS_PARAM(fd), + method_open_machine_root_directory, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetMachineUIDShift", + "s", + SD_BUS_PARAM(name), + "u", + SD_BUS_PARAM(shift), + method_get_machine_uid_shift, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("RemoveImage", + "s", + SD_BUS_PARAM(name), + NULL,, + method_remove_image, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("RenameImage", + "ss", + SD_BUS_PARAM(name) + SD_BUS_PARAM(new_name), + NULL,, + method_rename_image, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CloneImage", + "ssb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(new_name) + SD_BUS_PARAM(read_only), + NULL,, + method_clone_image, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MarkImageReadOnly", + "sb", + SD_BUS_PARAM(name) + SD_BUS_PARAM(read_only), + NULL,, + method_mark_image_read_only, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetImageHostname", + "s", + SD_BUS_PARAM(name), + "s", + SD_BUS_PARAM(hostname), + method_get_image_hostname, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetImageMachineID", + "s", + SD_BUS_PARAM(name), + "ay", + SD_BUS_PARAM(id), + method_get_image_machine_id, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetImageMachineInfo", + "s", + SD_BUS_PARAM(name), + "a{ss}", + SD_BUS_PARAM(machine_info), + method_get_image_machine_info, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetImageOSRelease", + "s", + SD_BUS_PARAM(name), + "a{ss}", + SD_BUS_PARAM(os_release), + method_get_image_os_release, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetPoolLimit", + "t", + SD_BUS_PARAM(size), + NULL,, + method_set_pool_limit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetImageLimit", + "st", + SD_BUS_PARAM(name) + SD_BUS_PARAM(size), + NULL,, + method_set_image_limit, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("CleanPool", + "s", + SD_BUS_PARAM(mode), + "a(st)", + SD_BUS_PARAM(images), + method_clean_pool, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MapFromMachineUser", + "su", + SD_BUS_PARAM(name) + SD_BUS_PARAM(uid_inner), + "u", + SD_BUS_PARAM(uid_outer), + method_map_from_machine_user, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MapToMachineUser", + "u", + SD_BUS_PARAM(uid_outer), + "sou", + SD_BUS_PARAM(machine_name) + SD_BUS_PARAM(machine_path) + SD_BUS_PARAM(uid_inner), + method_map_to_machine_user, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MapFromMachineGroup", + "su", + SD_BUS_PARAM(name) + SD_BUS_PARAM(gid_inner), + "u", + SD_BUS_PARAM(gid_outer), + method_map_from_machine_group, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("MapToMachineGroup", + "u", + SD_BUS_PARAM(gid_outer), + "sou", + SD_BUS_PARAM(machine_name) + SD_BUS_PARAM(machine_path) + SD_BUS_PARAM(gid_inner), + method_map_to_machine_group, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL_WITH_NAMES("MachineNew", + "so", + SD_BUS_PARAM(machine) + SD_BUS_PARAM(path), + 0), + SD_BUS_SIGNAL_WITH_NAMES("MachineRemoved", + "so", + SD_BUS_PARAM(machine) + SD_BUS_PARAM(path), + 0), + SD_BUS_VTABLE_END }; +const BusObjectImplementation manager_object = { + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + .vtables = BUS_VTABLES(manager_vtable), + .children = BUS_IMPLEMENTATIONS( &machine_object, + &image_object ), +}; + int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *path, *result, *unit; Manager *m = userdata; @@ -1303,16 +1452,7 @@ int manager_unref_unit( assert(m); assert(unit); - return sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnrefUnit", - error, - NULL, - "s", - unit); + return bus_call_method(m->bus, bus_systemd_mgr, "UnrefUnit", error, NULL, "s", unit); } int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { @@ -1322,15 +1462,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c assert(manager); assert(unit); - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StopUnit", - error, - &reply, - "ss", unit, "fail"); + r = bus_call_method(manager->bus, bus_systemd_mgr, "StopUnit", error, &reply, "ss", unit, "fail"); if (r < 0) { if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { @@ -1367,15 +1499,7 @@ int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_erro assert(manager); assert(unit); - return sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - error, - NULL, - "ssi", unit, "all", signo); + return bus_call_method(manager->bus, bus_systemd_mgr, "KillUnit", error, NULL, "ssi", unit, "all", signo); } int manager_unit_is_active(Manager *manager, const char *unit) { diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c new file mode 100644 index 000000000..058ee5c8e --- /dev/null +++ b/src/machine/machined-varlink.c @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "format-util.h" +#include "machined-varlink.h" +#include "mkdir.h" +#include "user-util.h" +#include "varlink.h" + +typedef struct LookupParameters { + const char *user_name; + const char *group_name; + union { + uid_t uid; + gid_t gid; + }; + const char *service; +} LookupParameters; + +static int build_user_json(const char *user_name, uid_t uid, const char *real_name, JsonVariant **ret) { + assert(user_name); + assert(uid_is_valid(uid)); + assert(ret); + + return json_build(ret, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)), + JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)), + JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)), + JSON_BUILD_PAIR_CONDITION(!isempty(real_name), "realName", JSON_BUILD_STRING(real_name)), + JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/")), + JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)), + JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)), + JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")), + JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container")))))); +} + +static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) { + assert(p); + + if (p->user_name && !streq(name, p->user_name)) + return false; + + if (uid_is_valid(p->uid) && uid != p->uid) + return false; + + return true; +} + +static int user_lookup_uid(Manager *m, uid_t uid, char **ret_name, char **ret_real_name) { + _cleanup_free_ char *n = NULL, *rn = NULL; + uid_t converted_uid; + Machine *machine; + int r; + + assert(m); + assert(uid_is_valid(uid)); + assert(ret_name); + assert(ret_real_name); + + if (uid < 0x10000) /* Host UID range */ + return -ESRCH; + + r = manager_find_machine_for_uid(m, uid, &machine, &converted_uid); + if (r < 0) + return r; + if (!r) + return -ESRCH; + + if (asprintf(&n, "vu-%s-" UID_FMT, machine->name, converted_uid) < 0) + return -ENOMEM; + + /* Don't synthesize invalid user/group names (too long...) */ + if (!valid_user_group_name(n, 0)) + return -ESRCH; + + if (asprintf(&rn, "UID " UID_FMT " of Container %s", converted_uid, machine->name) < 0) + return -ENOMEM; + + /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */ + if (!valid_gecos(rn)) + rn = mfree(rn); + + *ret_name = TAKE_PTR(n); + *ret_real_name = TAKE_PTR(rn); + return 0; +} + +static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char **ret_real_name) { + _cleanup_free_ char *mn = NULL, *rn = NULL; + uid_t uid, converted_uid; + Machine *machine; + const char *e, *d; + int r; + + assert(m); + + if (!valid_user_group_name(name, 0)) + return -ESRCH; + + e = startswith(name, "vu-"); + if (!e) + return -ESRCH; + + d = strrchr(e, '-'); + if (!d) + return -ESRCH; + + if (parse_uid(d + 1, &uid) < 0) + return -ESRCH; + + mn = strndup(e, d - e); + if (!mn) + return -ENOMEM; + + machine = hashmap_get(m->machines, mn); + if (!machine) + return -ESRCH; + + if (machine->class != MACHINE_CONTAINER) + return -ESRCH; + + r = machine_translate_uid(machine, uid, &converted_uid); + if (r < 0) + return r; + + if (asprintf(&rn, "UID " UID_FMT " of Container %s", uid, machine->name) < 0) + return -ENOMEM; + if (!valid_gecos(rn)) + rn = mfree(rn); + + *ret_uid = converted_uid; + *ret_real_name = TAKE_PTR(rn); + return 0; +} + +static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { + { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 }, + { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, + { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + LookupParameters p = { + .uid = UID_INVALID, + }; + _cleanup_free_ char *found_name = NULL, *found_real_name = NULL; + uid_t found_uid = UID_INVALID, uid; + Manager *m = userdata; + const char *un; + int r; + + assert(parameters); + assert(m); + + r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); + if (r < 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) + return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); + + if (uid_is_valid(p.uid)) + r = user_lookup_uid(m, p.uid, &found_name, &found_real_name); + else if (p.user_name) + r = user_lookup_name(m, p.user_name, &found_uid, &found_real_name); + else + return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL); + if (r == -ESRCH) + return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); + if (r < 0) + return r; + + uid = uid_is_valid(found_uid) ? found_uid : p.uid; + un = found_name ?: p.user_name; + + if (!user_match_lookup_parameters(&p, un, uid)) + return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL); + + r = build_user_json(un, uid, found_real_name, &v); + if (r < 0) + return r; + + return varlink_reply(link, v); +} + +static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) { + assert(group_name); + assert(gid_is_valid(gid)); + assert(ret); + + return json_build(ret, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)), + JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)), + JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")), + JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container")))))); + } + +static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) { + assert(p); + + if (p->group_name && !streq(name, p->group_name)) + return false; + + if (gid_is_valid(p->gid) && gid != p->gid) + return false; + + return true; +} + +static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) { + _cleanup_free_ char *n = NULL; + gid_t converted_gid; + Machine *machine; + int r; + + assert(m); + assert(gid_is_valid(gid)); + assert(ret_name); + + if (gid < 0x10000) /* Host GID range */ + return -ESRCH; + + r = manager_find_machine_for_gid(m, gid, &machine, &converted_gid); + if (r < 0) + return r; + if (!r) + return -ESRCH; + + if (asprintf(&n, "vg-%s-" GID_FMT, machine->name, converted_gid) < 0) + return -ENOMEM; + + if (!valid_user_group_name(n, 0)) + return -ESRCH; + + *ret_name = TAKE_PTR(n); + return 0; +} + +static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid) { + _cleanup_free_ char *mn = NULL; + gid_t gid, converted_gid; + Machine *machine; + const char *e, *d; + int r; + + assert(m); + + if (!valid_user_group_name(name, 0)) + return -ESRCH; + + e = startswith(name, "vg-"); + if (!e) + return -ESRCH; + + d = strrchr(e, '-'); + if (!d) + return -ESRCH; + + if (parse_gid(d + 1, &gid) < 0) + return -ESRCH; + + mn = strndup(e, d - e); + if (!mn) + return -ENOMEM; + + machine = hashmap_get(m->machines, mn); + if (!machine) + return -ESRCH; + + if (machine->class != MACHINE_CONTAINER) + return -ESRCH; + + r = machine_translate_gid(machine, gid, &converted_gid); + if (r < 0) + return r; + + *ret_gid = converted_gid; + return 0; +} + +static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { + { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 }, + { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, + { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + LookupParameters p = { + .gid = GID_INVALID, + }; + _cleanup_free_ char *found_name = NULL; + uid_t found_gid = GID_INVALID, gid; + Manager *m = userdata; + const char *gn; + int r; + + assert(parameters); + assert(m); + + r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); + if (r < 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) + return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); + + if (gid_is_valid(p.gid)) + r = group_lookup_gid(m, p.gid, &found_name); + else if (p.group_name) + r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid); + else + return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL); + if (r == -ESRCH) + return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); + if (r < 0) + return r; + + gid = gid_is_valid(found_gid) ? found_gid : p.gid; + gn = found_name ?: p.group_name; + + if (!group_match_lookup_parameters(&p, gn, gid)) + return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL); + + r = build_group_json(gn, gid, &v); + if (r < 0) + return r; + + return varlink_reply(link, v); +} + +static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + + static const JsonDispatch dispatch_table[] = { + { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE }, + { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE }, + { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 }, + {} + }; + + LookupParameters p = {}; + int r; + + assert(parameters); + + r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); + if (r < 0) + return r; + + if (!streq_ptr(p.service, "io.systemd.Machine")) + return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); + + /* We don't support auxiliary groups for machines. */ + return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); +} + +int manager_varlink_init(Manager *m) { + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + int r; + + assert(m); + + if (m->varlink_server) + return 0; + + r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID); + if (r < 0) + return log_error_errno(r, "Failed to allocate varlink server object: %m"); + + varlink_server_set_userdata(s, m); + + r = varlink_server_bind_method_many( + s, + "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record, + "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record, + "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships); + if (r < 0) + return log_error_errno(r, "Failed to register varlink methods: %m"); + + (void) mkdir_p("/run/systemd/userdb", 0755); + + r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.Machine", 0666); + if (r < 0) + return log_error_errno(r, "Failed to bind to varlink socket: %m"); + + r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); + + m->varlink_server = TAKE_PTR(s); + return 0; +} + +void manager_varlink_done(Manager *m) { + assert(m); + + m->varlink_server = varlink_server_unref(m->varlink_server); +} diff --git a/src/machine/machined-varlink.h b/src/machine/machined-varlink.h new file mode 100644 index 000000000..42e1c3f4a --- /dev/null +++ b/src/machine/machined-varlink.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "machined.h" + +int manager_varlink_init(Manager *m); +void manager_varlink_done(Manager *m); diff --git a/src/machine/machined.c b/src/machine/machined.c index ace2131c2..a884f3de9 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -10,6 +10,8 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "cgroup-util.h" #include "dirent-util.h" @@ -18,9 +20,11 @@ #include "hostname-util.h" #include "label.h" #include "machine-image.h" +#include "machined-varlink.h" #include "machined.h" #include "main-func.h" #include "process-util.h" +#include "service-util.h" #include "signal-util.h" #include "special.h" @@ -83,6 +87,8 @@ static Manager* manager_unref(Manager *m) { bus_verify_polkit_async_registry_free(m->polkit_registry); + manager_varlink_done(m); + sd_bus_flush_close_unref(m->bus); sd_event_unref(m->event); @@ -187,45 +193,15 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m); + r = bus_add_implementation(m->bus, &manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); + return r; - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add machine object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add machine enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add image object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add image enumerator: %m"); - - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "JobRemoved", - match_job_removed, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitRemoved", - match_unit_removed, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); @@ -240,29 +216,18 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); - r = sd_bus_match_signal_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reloading", - match_reloading, NULL, m); + r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m); if (r < 0) return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method_async( - m->bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe", - NULL, NULL, - NULL); + r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to enable subscription: %m"); + r = bus_log_control_api_register(m->bus); + if (r < 0) + return r; + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to request name: %m"); @@ -310,6 +275,11 @@ static int manager_startup(Manager *m) { if (r < 0) return r; + /* Set up Varlink service */ + r = manager_varlink_init(m); + if (r < 0) + return r; + /* Deserialize state */ manager_enumerate_machines(m); @@ -329,6 +299,9 @@ static bool check_idle(void *userdata) { if (m->operations) return false; + if (varlink_server_current_connections(m->varlink_server) > 0) + return false; + manager_gc(m, true); return hashmap_isempty(m->machines); @@ -352,12 +325,15 @@ static int run(int argc, char *argv[]) { log_set_facility(LOG_AUTH); log_setup_service(); - umask(0022); + r = service_parse_argv("systemd-machined.service", + "Manage registrations of local VMs and containers.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) { - log_error("This program takes no arguments."); - return -EINVAL; - } + umask(0022); /* Always create the directories people can create inotify watches in. Note that some applications might check * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always diff --git a/src/machine/machined.h b/src/machine/machined.h index 205d90f83..c83017acb 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -6,15 +6,15 @@ #include "sd-bus.h" #include "sd-event.h" -#include "hashmap.h" -#include "list.h" - typedef struct Manager Manager; +#include "hashmap.h" #include "image-dbus.h" +#include "list.h" #include "machine-dbus.h" #include "machine.h" #include "operation.h" +#include "varlink.h" struct Manager { sd_event *event; @@ -37,12 +37,14 @@ struct Manager { unsigned n_operations; sd_event_source *nscd_cache_flush_event; + + VarlinkServer *varlink_server; }; int manager_add_machine(Manager *m, const char *name, Machine **_machine); int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine); -extern const sd_bus_vtable manager_vtable[]; +extern const BusObjectImplementation manager_object; int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); @@ -56,3 +58,6 @@ int manager_unit_is_active(Manager *manager, const char *unit); int manager_job_is_active(Manager *manager, const char *path); int manager_enqueue_nscd_cache_flush(Manager *m); + +int manager_find_machine_for_uid(Manager *m, uid_t host_uid, Machine **ret_machine, uid_t *ret_internal_uid); +int manager_find_machine_for_gid(Manager *m, gid_t host_gid, Machine **ret_machine, gid_t *ret_internal_gid); diff --git a/src/machine/meson.build b/src/machine/meson.build index bc670714b..291630ece 100644 --- a/src/machine/meson.build +++ b/src/machine/meson.build @@ -6,14 +6,16 @@ systemd_machined_sources = files(''' '''.split()) libmachine_core_sources = files(''' - machine.c - machine.h - machined-dbus.c - machined-core.c - machine-dbus.c - machine-dbus.h image-dbus.c image-dbus.h + machine-dbus.c + machine-dbus.h + machine.c + machine.h + machined-core.c + machined-dbus.c + machined-varlink.c + machined-varlink.h operation.c operation.h '''.split()) diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index 982e0a285..22da18968 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -6,8 +6,8 @@ #include "sd-device.h" #include "bus-error.h" +#include "bus-locator.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "bus-wait-for-jobs.h" #include "device-util.h" #include "dirent-util.h" @@ -364,7 +364,7 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "At most two arguments required."); - if (arg_mount_type && (fstype_is_api_vfs(arg_mount_type) || fstype_is_network(arg_mount_type))) { + if (arg_mount_type && !fstype_is_blockdev_backed(arg_mount_type)) { arg_mount_what = strdup(argv[optind]); if (!arg_mount_what) return log_oom(); @@ -551,13 +551,7 @@ static int start_transient_mount( if (r < 0) return log_error_errno(r, "Failed to make mount unit name: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -638,13 +632,7 @@ static int start_transient_automount( if (r < 0) return log_error_errno(r, "Failed to make mount unit name: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -854,13 +842,7 @@ static int stop_mount( if (r < 0) return log_error_errno(r, "Failed to make %s unit name from path %s: %m", suffix + 1, where); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StopUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StopUnit"); if (r < 0) return bus_log_create_error(r); @@ -954,10 +936,9 @@ static int umount_by_device(sd_bus *bus, const char *what) { if (r < 0) return log_device_error_errno(d, r, "Failed to get device property: %m"); - if (!streq(v, "filesystem")) { - log_device_error(d, "%s does not contain a known file system.", what); - return -EINVAL; - } + if (!streq(v, "filesystem")) + return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL), + "%s does not contain a known file system.", what); if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0) r2 = stop_mounts(bus, v); @@ -1293,10 +1274,9 @@ static int discover_loop_backing_file(void) { if (r < 0) return log_error_errno(r, "Failed to get device from device number: %m"); - if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem")) { - log_device_error(d, "%s does not contain a known file system.", arg_mount_what); - return -EINVAL; - } + if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem")) + return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL), + "%s does not contain a known file system.", arg_mount_what); r = acquire_mount_type(d); if (r < 0) @@ -1454,7 +1434,7 @@ static int list_devices(void) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); return 0; } @@ -1476,12 +1456,12 @@ static int run(int argc, char* argv[]) { r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); if (arg_action == ACTION_UMOUNT) return action_umount(bus, argc, argv); - if ((!arg_mount_type || !fstype_is_network(arg_mount_type)) + if ((!arg_mount_type || fstype_is_blockdev_backed(arg_mount_type)) && !path_is_normalized(arg_mount_what)) { log_error("Path contains non-normalized components: %s", arg_mount_what); return -EINVAL; diff --git a/src/network/meson.build b/src/network/meson.build index c1c02cfda..b3a88d991 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -61,6 +61,8 @@ sources = files(''' networkd-conf.h networkd-dhcp-common.c networkd-dhcp-common.h + networkd-dhcp-server-bus.c + networkd-dhcp-server-bus.h networkd-dhcp-server.c networkd-dhcp-server.h networkd-dhcp4.c @@ -103,26 +105,52 @@ sources = files(''' networkd-routing-policy-rule.h networkd-speed-meter.c networkd-speed-meter.h + networkd-sriov.c + networkd-sriov.h networkd-util.c networkd-util.h networkd-wifi.c networkd-wifi.h + tc/cake.c + tc/cake.h tc/codel.c tc/codel.h + tc/drr.c + tc/drr.h + tc/ets.c + tc/ets.h + tc/fifo.c + tc/fifo.h tc/fq.c tc/fq.h tc/fq-codel.c tc/fq-codel.h + tc/gred.c + tc/gred.h + tc/hhf.c + tc/hhf.h + tc/htb.c + tc/htb.h tc/netem.c tc/netem.h + tc/pie.c + tc/pie.h tc/qdisc.c tc/qdisc.h + tc/qfq.c + tc/qfq.h + tc/sfb.c + tc/sfb.h tc/sfq.c tc/sfq.h tc/tbf.c tc/tbf.h tc/tc-util.c tc/tc-util.h + tc/tc.c + tc/tc.h + tc/tclass.c + tc/tclass.h tc/teql.c tc/teql.h '''.split()) diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c index 8df39e358..b5cf16270 100644 --- a/src/network/netdev/bond.c +++ b/src/network/netdev/bond.c @@ -2,13 +2,13 @@ #include "alloc-util.h" #include "bond.h" +#include "bond-util.h" #include "conf-parser.h" #include "ether-addr-util.h" #include "extract-word.h" #include "netlink-util.h" #include "networkd-manager.h" #include "string-table.h" -#include "string-util.h" /* * Number of seconds between instances where the bonding @@ -41,84 +41,16 @@ #define GRATUITOUS_ARP_MAX 255 #define GRATUITOUS_ARP_DEFAULT 1 -static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = { - [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr", - [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup", - [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor", - [NETDEV_BOND_MODE_BROADCAST] = "broadcast", - [NETDEV_BOND_MODE_802_3AD] = "802.3ad", - [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb", - [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode"); - -static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = { - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2", - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4", - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3", - [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3", - [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_xmit_hash_policy, bond_xmit_hash_policy, BondXmitHashPolicy, - "Failed to parse bond transmit hash policy") - -static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = { - [NETDEV_BOND_LACP_RATE_SLOW] = "slow", - [NETDEV_BOND_LACP_RATE_FAST] = "fast", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate") - -static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = { - [NETDEV_BOND_AD_SELECT_STABLE] = "stable", - [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth", - [NETDEV_BOND_AD_SELECT_COUNT] = "count", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect); + "Failed to parse bond transmit hash policy"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate"); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select"); - -static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = { - [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none", - [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active", - [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC"); - -static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = { - [NETDEV_BOND_ARP_VALIDATE_NONE] = "none", - [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active", - [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup", - [NETDEV_BOND_ARP_VALIDATE_ALL]= "all", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate"); - -static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = { - [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any", - [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets"); - -static const char *const bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { - [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always", - [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better", - [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect"); static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { @@ -356,7 +288,7 @@ int link_set_bond(Link *link) { r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_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_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -388,14 +320,16 @@ int config_parse_arp_ip_target_address( return 0; } - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *n = NULL; union in_addr_union ip; - r = extract_first_word(&rvalue, &n, NULL, 0); + r = extract_first_word(&p, &n, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse Bond ARP ip target address, ignoring assignment: %s", + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse Bond ARP IP target address, ignoring assignment: %s", rvalue); return 0; } @@ -404,8 +338,8 @@ int config_parse_arp_ip_target_address( r = in_addr_from_string(AF_INET, n, &ip); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Bond ARP ip target address is invalid, ignoring assignment: %s", n); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Bond ARP IP target address is invalid, ignoring assignment: %s", n); continue; } @@ -415,7 +349,7 @@ int config_parse_arp_ip_target_address( if (ordered_set_size(b->arp_ip_targets) >= NETDEV_BOND_ARP_TARGETS_MAX) { log_syntax(unit, LOG_WARNING, filename, line, 0, - "Too many ARP ip targets are specified. The maximum number is %d. Ignoring assignment: %s", + "Too many ARP IP targets are specified. The maximum number is %d. Ignoring assignment: %s", NETDEV_BOND_ARP_TARGETS_MAX, n); continue; } @@ -423,10 +357,10 @@ int config_parse_arp_ip_target_address( r = ordered_set_put(b->arp_ip_targets, UINT32_TO_PTR(ip.in.s_addr)); if (r == -EEXIST) log_syntax(unit, LOG_WARNING, filename, line, r, - "Bond ARP ip target address is duplicated, ignoring assignment: %s", n); + "Bond ARP IP target address is duplicated, ignoring assignment: %s", n); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store bond ARP ip target address '%s', ignoring assignment: %m", n); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store bond ARP IP target address '%s', ignoring assignment: %m", n); } } @@ -452,13 +386,13 @@ int config_parse_ad_actor_sys_prio( r = safe_atou16(rvalue, &v); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse actor system priority '%s', ignoring: %m", rvalue); return 0; } if (v == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse actor system priority '%s'. Range is [1,65535], ignoring.", rvalue); return 0; @@ -491,13 +425,13 @@ int config_parse_ad_user_port_key( r = safe_atou16(rvalue, &v); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse user port key '%s', ignoring: %m", rvalue); return 0; } if (v > 1023) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse user port key '%s'. Range is [0…1023], ignoring.", rvalue); return 0; } @@ -529,13 +463,13 @@ int config_parse_ad_actor_system( r = ether_addr_from_string(rvalue, &n); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address %s. Ignoring assignment: %m", rvalue); return 0; } if (ether_addr_is_null(&n) || (n.ether_addr_octet[0] & 0x01)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Not a valid MAC address %s, can not be null or multicast. Ignoring assignment.", rvalue); return 0; diff --git a/src/network/netdev/bond.h b/src/network/netdev/bond.h index 28796a3a8..64b2dd04a 100644 --- a/src/network/netdev/bond.h +++ b/src/network/netdev/bond.h @@ -4,85 +4,11 @@ #include #include -#include "in-addr-util.h" +#include "bond-util.h" +#include "macro.h" #include "netdev.h" #include "ordered-set.h" -/* - * Maximum number of targets supported by the kernel for a single - * bond netdev. - */ -#define NETDEV_BOND_ARP_TARGETS_MAX 16 - -typedef enum BondMode { - NETDEV_BOND_MODE_BALANCE_RR = BOND_MODE_ROUNDROBIN, - NETDEV_BOND_MODE_ACTIVE_BACKUP = BOND_MODE_ACTIVEBACKUP, - NETDEV_BOND_MODE_BALANCE_XOR = BOND_MODE_XOR, - NETDEV_BOND_MODE_BROADCAST = BOND_MODE_BROADCAST, - NETDEV_BOND_MODE_802_3AD = BOND_MODE_8023AD, - NETDEV_BOND_MODE_BALANCE_TLB = BOND_MODE_TLB, - NETDEV_BOND_MODE_BALANCE_ALB = BOND_MODE_ALB, - _NETDEV_BOND_MODE_MAX, - _NETDEV_BOND_MODE_INVALID = -1 -} BondMode; - -typedef enum BondXmitHashPolicy { - NETDEV_BOND_XMIT_HASH_POLICY_LAYER2 = BOND_XMIT_POLICY_LAYER2, - NETDEV_BOND_XMIT_HASH_POLICY_LAYER34 = BOND_XMIT_POLICY_LAYER34, - NETDEV_BOND_XMIT_HASH_POLICY_LAYER23 = BOND_XMIT_POLICY_LAYER23, - NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23 = BOND_XMIT_POLICY_ENCAP23, - NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34 = BOND_XMIT_POLICY_ENCAP34, - _NETDEV_BOND_XMIT_HASH_POLICY_MAX, - _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1 -} BondXmitHashPolicy; - -typedef enum BondLacpRate { - NETDEV_BOND_LACP_RATE_SLOW, - NETDEV_BOND_LACP_RATE_FAST, - _NETDEV_BOND_LACP_RATE_MAX, - _NETDEV_BOND_LACP_RATE_INVALID = -1, -} BondLacpRate; - -typedef enum BondAdSelect { - NETDEV_BOND_AD_SELECT_STABLE, - NETDEV_BOND_AD_SELECT_BANDWIDTH, - NETDEV_BOND_AD_SELECT_COUNT, - _NETDEV_BOND_AD_SELECT_MAX, - _NETDEV_BOND_AD_SELECT_INVALID = -1, -} BondAdSelect; - -typedef enum BondFailOverMac { - NETDEV_BOND_FAIL_OVER_MAC_NONE, - NETDEV_BOND_FAIL_OVER_MAC_ACTIVE, - NETDEV_BOND_FAIL_OVER_MAC_FOLLOW, - _NETDEV_BOND_FAIL_OVER_MAC_MAX, - _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1, -} BondFailOverMac; - -typedef enum BondArpValidate { - NETDEV_BOND_ARP_VALIDATE_NONE, - NETDEV_BOND_ARP_VALIDATE_ACTIVE, - NETDEV_BOND_ARP_VALIDATE_BACKUP, - NETDEV_BOND_ARP_VALIDATE_ALL, - _NETDEV_BOND_ARP_VALIDATE_MAX, - _NETDEV_BOND_ARP_VALIDATE_INVALID = -1, -} BondArpValidate; - -typedef enum BondArpAllTargets { - NETDEV_BOND_ARP_ALL_TARGETS_ANY, - NETDEV_BOND_ARP_ALL_TARGETS_ALL, - _NETDEV_BOND_ARP_ALL_TARGETS_MAX, - _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1, -} BondArpAllTargets; - -typedef enum BondPrimaryReselect { - NETDEV_BOND_PRIMARY_RESELECT_ALWAYS, - NETDEV_BOND_PRIMARY_RESELECT_BETTER, - NETDEV_BOND_PRIMARY_RESELECT_FAILURE, - _NETDEV_BOND_PRIMARY_RESELECT_MAX, - _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1, -} BondPrimaryReselect; - typedef struct Bond { NetDev meta; @@ -122,30 +48,6 @@ extern const NetDevVTable bond_vtable; int link_set_bond(Link *link); -const char *bond_mode_to_string(BondMode d) _const_; -BondMode bond_mode_from_string(const char *d) _pure_; - -const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_; -BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_; - -const char *bond_lacp_rate_to_string(BondLacpRate d) _const_; -BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_; - -const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_; -BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_; - -const char *bond_ad_select_to_string(BondAdSelect d) _const_; -BondAdSelect bond_ad_select_from_string(const char *d) _pure_; - -const char *bond_arp_validate_to_string(BondArpValidate d) _const_; -BondArpValidate bond_arp_validate_from_string(const char *d) _pure_; - -const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_; -BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_; - -const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_; -BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_; - CONFIG_PARSER_PROTOTYPE(config_parse_bond_mode); CONFIG_PARSER_PROTOTYPE(config_parse_bond_xmit_hash_policy); CONFIG_PARSER_PROTOTYPE(config_parse_bond_lacp_rate); diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index 6b8f99446..70240661e 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -126,6 +126,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m"); } + if (b->vlan_protocol >= 0) { + r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_PROTOCOL, b->vlan_protocol); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_PROTOCOL attribute: %m"); + } + if (b->stp >= 0) { r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp); if (r < 0) @@ -212,7 +218,7 @@ int link_set_bridge(Link *link) { return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); } - if (link->network->allow_port_to_be_root >= 0) { + 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_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); @@ -320,13 +326,13 @@ int config_parse_bridge_igmp_version( r = safe_atou8(rvalue, &u); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse bridge's multicast IGMP version number '%s', ignoring assignment: %m", rvalue); return 0; } if (!IN_SET(u, 2, 3)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid bridge's multicast IGMP version number '%s', ignoring assignment.", rvalue); return 0; } @@ -346,6 +352,7 @@ static void bridge_init(NetDev *n) { b->mcast_querier = -1; b->mcast_snooping = -1; b->vlan_filtering = -1; + b->vlan_protocol = -1; b->stp = -1; b->default_pvid = VLANID_INVALID; b->forward_delay = USEC_INFINITY; diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h index b0a728e5a..ed4f484c9 100644 --- a/src/network/netdev/bridge.h +++ b/src/network/netdev/bridge.h @@ -13,6 +13,7 @@ typedef struct Bridge { int mcast_querier; int mcast_snooping; int vlan_filtering; + int vlan_protocol; int stp; uint16_t priority; uint16_t group_fwd_mask; diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c index bc0dc185d..832cf57de 100644 --- a/src/network/netdev/fou-tunnel.c +++ b/src/network/netdev/fou-tunnel.c @@ -167,14 +167,14 @@ int config_parse_ip_protocol( else { r = safe_atou(rvalue, &protocol); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IP protocol '%s' for FooOverUDP tunnel, " "ignoring assignment: %m", rvalue); return 0; } if (protocol > UINT8_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "IP protocol '%s' for FooOverUDP tunnel out of range, " "ignoring assignment: %m", rvalue); return 0; @@ -212,7 +212,7 @@ int config_parse_fou_tunnel_address( r = in_addr_from_string_auto(rvalue, f, addr); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "FooOverUDP tunnel '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index b960840a5..b4f865a53 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -185,12 +185,12 @@ int config_parse_geneve_vni(const char *unit, r = safe_atou32(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve VNI '%s'.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Geneve VNI '%s'.", rvalue); return 0; } if (f > GENEVE_VID_MAX){ - log_syntax(unit, LOG_ERR, filename, line, r, "Geneve VNI out is of range '%s'.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Geneve VNI out is of range '%s'.", rvalue); return 0; } @@ -220,13 +220,13 @@ int config_parse_geneve_address(const char *unit, r = in_addr_from_string_auto(rvalue, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "geneve '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "geneve '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); return 0; } r = in_addr_is_multicast(f, &buffer); if (r > 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "geneve invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "geneve invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -257,12 +257,12 @@ int config_parse_geneve_flow_label(const char *unit, r = safe_atou32(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve flow label '%s'.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Geneve flow label '%s'.", rvalue); return 0; } if (f & ~GENEVE_FLOW_LABEL_MAX_MASK) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Geneve flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue); return 0; } @@ -296,13 +296,13 @@ int config_parse_geneve_ttl(const char *unit, else { r = safe_atou(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Geneve TTL '%s', ignoring assignment: %m", rvalue); return 0; } if (f > 255) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid Geneve TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue); return 0; } diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c index 1d87cfa86..bd14f625d 100644 --- a/src/network/netdev/ipvlan.c +++ b/src/network/netdev/ipvlan.c @@ -4,25 +4,11 @@ #include "conf-parser.h" #include "ipvlan.h" +#include "ipvlan-util.h" #include "networkd-link.h" -#include "string-table.h" +#include "string-util.h" -static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = { - [NETDEV_IPVLAN_MODE_L2] = "L2", - [NETDEV_IPVLAN_MODE_L3] = "L3", - [NETDEV_IPVLAN_MODE_L3S] = "L3S", -}; - -DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode"); - -static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = { - [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge", - [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private", - [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa", -}; - -DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags); DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_flags, ipvlan_flags, IPVlanFlags, "Failed to parse ipvlan flags"); static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h index 171407b7c..8e658184f 100644 --- a/src/network/netdev/ipvlan.h +++ b/src/network/netdev/ipvlan.h @@ -4,24 +4,9 @@ #include #include +#include "ipvlan-util.h" #include "netdev.h" -typedef enum IPVlanMode { - NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, - NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3, - NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S, - _NETDEV_IPVLAN_MODE_MAX, - _NETDEV_IPVLAN_MODE_INVALID = -1 -} IPVlanMode; - -typedef enum IPVlanFlags { - NETDEV_IPVLAN_FLAGS_BRIGDE, - NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE, - NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA, - _NETDEV_IPVLAN_FLAGS_MAX, - _NETDEV_IPVLAN_FLAGS_INVALID = -1 -} IPVlanFlags; - typedef struct IPVlan { NetDev meta; @@ -34,12 +19,6 @@ DEFINE_NETDEV_CAST(IPVTAP, IPVlan); extern const NetDevVTable ipvlan_vtable; extern const NetDevVTable ipvtap_vtable; -const char *ipvlan_mode_to_string(IPVlanMode d) _const_; -IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; - -const char *ipvlan_flags_to_string(IPVlanFlags d) _const_; -IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_; - CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode); CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags); diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c index 91788c368..0d670cb2a 100644 --- a/src/network/netdev/l2tp-tunnel.c +++ b/src/network/netdev/l2tp-tunnel.c @@ -459,7 +459,7 @@ int config_parse_l2tp_tunnel_address( else r = in_addr_from_string(t->family, rvalue, addr); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid L2TP Tunnel address specified in %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -489,13 +489,13 @@ int config_parse_l2tp_tunnel_id( r = safe_atou32(rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse L2TP tunnel id. Ignoring assignment: %s", rvalue); return 0; } if (k == 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid L2TP tunnel id. Ignoring assignment: %s", rvalue); return 0; } @@ -530,17 +530,17 @@ int config_parse_l2tp_session_id( r = l2tp_session_new_static(t, filename, section_line, &session); if (r < 0) - return r; + return log_oom(); r = safe_atou32(rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse L2TP session id. Ignoring assignment: %s", rvalue); return 0; } if (k == 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid L2TP session id. Ignoring assignment: %s", rvalue); return 0; } @@ -579,11 +579,11 @@ int config_parse_l2tp_session_l2spec( r = l2tp_session_new_static(t, filename, section_line, &session); if (r < 0) - return r; + return log_oom(); spec = l2tp_l2spec_type_from_string(rvalue); if (spec < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse layer2 specific header type. Ignoring assignment: %s", rvalue); return 0; } @@ -618,10 +618,10 @@ int config_parse_l2tp_session_name( r = l2tp_session_new_static(t, filename, section_line, &session); if (r < 0) - return r; + return log_oom(); if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse L2TP tunnel session name. Ignoring assignment: %s", rvalue); return 0; } diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index 8f7559e9a..2ffa5ec8c 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -577,7 +577,7 @@ int config_parse_macsec_port( else if (streq(section, "MACsecReceiveChannel")) { r = macsec_receive_channel_new_static(s, filename, section_line, &c); if (r < 0) - return r; + return log_oom(); dest = &c->sci.port; } else { @@ -585,14 +585,14 @@ int config_parse_macsec_port( r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); dest = &b->sci.port; } r = parse_ip_port(rvalue, &port); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m", rvalue); return 0; @@ -634,11 +634,11 @@ int config_parse_macsec_hw_address( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse MAC address for secure channel identifier. " "Ignoring assignment: %s", rvalue); return 0; @@ -679,18 +679,18 @@ int config_parse_macsec_packet_number( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); dest = a ? &a->sa.packet_number : &b->sa.packet_number; r = safe_atou32(rvalue, &val); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse packet number. Ignoring assignment: %s", rvalue); return 0; } if (streq(section, "MACsecTransmitAssociation") && val == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid packet number. Ignoring assignment: %s", rvalue); return 0; } @@ -735,19 +735,19 @@ int config_parse_macsec_key( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); dest = a ? &a->sa : &b->sa; r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key. Ignoring assignment: %m"); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse key. Ignoring assignment: %m"); return 0; } if (l != 16) { /* See DEFAULT_SAK_LEN in drivers/net/macsec.c */ - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l); return 0; } @@ -791,7 +791,7 @@ int config_parse_macsec_key_file( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); dest = a ? &a->sa.key_file : &b->sa.key_file; @@ -845,15 +845,15 @@ int config_parse_macsec_key_id( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); r = unhexmem(rvalue, strlen(rvalue), &p, &l); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse KeyId \"%s\": %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse KeyId \"%s\": %m", rvalue); return 0; } if (l > MACSEC_KEYID_LEN) - return log_syntax(unit, LOG_ERR, filename, line, 0, + return log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified KeyId is larger then the allowed maximum (%zu > %u), ignoring: %s", l, MACSEC_KEYID_LEN, rvalue); @@ -896,7 +896,7 @@ int config_parse_macsec_sa_activate( else r = macsec_receive_association_new_static(s, filename, section_line, &b); if (r < 0) - return r; + return log_oom(); dest = a ? &a->sa.activate : &b->sa.activate; @@ -905,7 +905,7 @@ int config_parse_macsec_sa_activate( else { r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse activation mode of %s security association. " "Ignoring assignment: %s", streq(section, "MACsecTransmitAssociation") ? "transmit" : "receive", @@ -945,18 +945,20 @@ int config_parse_macsec_use_for_encoding( r = macsec_transmit_association_new_static(s, filename, section_line, &a); if (r < 0) - return r; + return log_oom(); - if (isempty(rvalue)) - r = -1; - else { - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse %s= setting. Ignoring assignment: %s", - lvalue, rvalue); - return 0; - } + if (isempty(rvalue)) { + a->sa.use_for_encoding = -1; + TAKE_PTR(a); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s= setting. Ignoring assignment: %s", + lvalue, rvalue); + return 0; } a->sa.use_for_encoding = r; @@ -981,7 +983,10 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) { (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0); - r = read_full_file_full(AT_FDCWD, sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len); + r = read_full_file_full( + AT_FDCWD, sa->key_file, + READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, + (char **) &key, &key_len); if (r < 0) return log_netdev_error_errno(netdev, r, "Failed to read key from '%s', ignoring: %m", diff --git a/src/network/netdev/macvlan.c b/src/network/netdev/macvlan.c index dbe25e9e3..41391788b 100644 --- a/src/network/netdev/macvlan.c +++ b/src/network/netdev/macvlan.c @@ -4,16 +4,8 @@ #include "conf-parser.h" #include "macvlan.h" -#include "string-table.h" +#include "macvlan-util.h" -static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = { - [NETDEV_MACVLAN_MODE_PRIVATE] = "private", - [NETDEV_MACVLAN_MODE_VEPA] = "vepa", - [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge", - [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru", -}; - -DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode"); static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { @@ -31,6 +23,29 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net assert(m); + if (m->mode == NETDEV_MACVLAN_MODE_SOURCE && !set_isempty(m->match_source_mac)) { + Iterator i; + const struct ether_addr *mac_addr; + + 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"); + + 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"); + + SET_FOREACH(mac_addr, m->match_source_mac, i) { + 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"); + } + + 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"); + } + if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); if (r < 0) @@ -40,6 +55,21 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net return 0; } +static void macvlan_done(NetDev *n) { + MacVlan *m; + + assert(n); + + if (n->kind == NETDEV_KIND_MACVLAN) + m = MACVLAN(n); + else + m = MACVTAP(n); + + assert(m); + + set_free_free(m->match_source_mac); +} + static void macvlan_init(NetDev *n) { MacVlan *m; @@ -58,6 +88,7 @@ static void macvlan_init(NetDev *n) { const NetDevVTable macvtap_vtable = { .object_size = sizeof(MacVlan), .init = macvlan_init, + .done = macvlan_done, .sections = NETDEV_COMMON_SECTIONS "MACVTAP\0", .fill_message_create = netdev_macvlan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, @@ -67,6 +98,7 @@ const NetDevVTable macvtap_vtable = { const NetDevVTable macvlan_vtable = { .object_size = sizeof(MacVlan), .init = macvlan_init, + .done = macvlan_done, .sections = NETDEV_COMMON_SECTIONS "MACVLAN\0", .fill_message_create = netdev_macvlan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, diff --git a/src/network/netdev/macvlan.h b/src/network/netdev/macvlan.h index 5d81be32d..7bc6eef12 100644 --- a/src/network/netdev/macvlan.h +++ b/src/network/netdev/macvlan.h @@ -3,21 +3,15 @@ typedef struct MacVlan MacVlan; +#include "macvlan-util.h" #include "netdev.h" - -typedef enum MacVlanMode { - NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, - NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA, - NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE, - NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU, - _NETDEV_MACVLAN_MODE_MAX, - _NETDEV_MACVLAN_MODE_INVALID = -1 -} MacVlanMode; +#include "set.h" struct MacVlan { NetDev meta; MacVlanMode mode; + Set *match_source_mac; }; DEFINE_NETDEV_CAST(MACVLAN, MacVlan); @@ -25,7 +19,4 @@ DEFINE_NETDEV_CAST(MACVTAP, MacVlan); extern const NetDevVTable macvlan_vtable; extern const NetDevVTable macvtap_vtable; -const char *macvlan_mode_to_string(MacVlanMode d) _const_; -MacVlanMode macvlan_mode_from_string(const char *d) _pure_; - CONFIG_PARSER_PROTOTYPE(config_parse_macvlan_mode); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 09a5f4822..0e2a9ce04 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -52,7 +52,9 @@ VLAN.MVRP, config_parse_tristate, VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVLAN.SourceMACAddress, config_parse_hwaddrs, 0, offsetof(MacVlan, match_source_mac) MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.SourceMACAddress, config_parse_hwaddrs, 0, offsetof(MacVlan, match_source_mac) IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) IPVTAP.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) @@ -206,6 +208,7 @@ Bridge.DefaultPVID, config_parse_default_port_vlanid, Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.VLANProtocol, config_parse_vlanprotocol, 0, offsetof(Bridge, vlan_protocol) Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) Bridge.MulticastIGMPVersion, config_parse_uint8, 0, offsetof(Bridge, igmp_version) VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 109122c32..446a580e2 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -2,6 +2,7 @@ #include #include +#include #include "alloc-util.h" #include "bond.h" @@ -24,6 +25,7 @@ #include "network-internal.h" #include "networkd-manager.h" #include "nlmon.h" +#include "path-lookup.h" #include "siphash24.h" #include "stat-util.h" #include "string-table.h" @@ -135,12 +137,12 @@ int config_parse_netdev_kind( k = netdev_kind_from_string(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue); return 0; } if (*kind != _NETDEV_KIND_INVALID && *kind != k) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified netdev kind is different from the previous value '%s', ignoring assignment: %s", netdev_kind_to_string(*kind), rvalue); return 0; @@ -362,7 +364,7 @@ static int netdev_enslave(NetDev *netdev, Link *link, link_netlink_message_handl if (r >= 0) callback(netdev->manager->rtnl, m, link); } else { - /* the netdev is not yet read, save this request for when it is */ + /* the netdev is not yet ready, save this request for when it is */ netdev_join_callback *cb; cb = new(netdev_join_callback, 1); @@ -684,15 +686,18 @@ int netdev_load_one(Manager *manager, const char *filename) { }; dropin_dirname = strjoina(basename(filename), ".d"); - r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, - NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS, - config_item_perf_lookup, network_netdev_gperf_lookup, - CONFIG_PARSE_WARN, netdev_raw, NULL); + r = config_parse_many( + filename, NETWORK_DIRS, dropin_dirname, + NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS, + config_item_perf_lookup, network_netdev_gperf_lookup, + CONFIG_PARSE_WARN, + netdev_raw, + NULL); if (r < 0) return r; /* skip out early if configuration does not match the environment */ - if (!condition_test_list(netdev_raw->conditions, NULL, NULL, NULL)) { + if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL)) { log_debug("%s: Conditions in the file do not match the system environment, skipping.", filename); return 0; } @@ -724,10 +729,12 @@ int netdev_load_one(Manager *manager, const char *filename) { if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); - r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, - NETDEV_VTABLE(netdev)->sections, - config_item_perf_lookup, network_netdev_gperf_lookup, - CONFIG_PARSE_WARN, netdev, NULL); + r = config_parse_many( + filename, NETWORK_DIRS, dropin_dirname, + NETDEV_VTABLE(netdev)->sections, + config_item_perf_lookup, network_netdev_gperf_lookup, + CONFIG_PARSE_WARN, + netdev, NULL); if (r < 0) return r; diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 72b33ed9e..852aa423b 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -541,13 +541,13 @@ int config_parse_tunnel_address(const char *unit, r = in_addr_from_string_auto(rvalue, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + 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_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue); return 0; } @@ -581,7 +581,7 @@ int config_parse_tunnel_key(const char *unit, if (r < 0) { r = safe_atou32(rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue); return 0; } } else @@ -626,7 +626,7 @@ int config_parse_ipv6_flowlabel(const char* unit, return r; if (k > 0xFFFFF) - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue); + 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; @@ -659,12 +659,12 @@ int config_parse_encap_limit(const char* unit, else { r = safe_atoi(rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue); + 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_ERR, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k); + 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; @@ -696,11 +696,11 @@ int config_parse_6rd_prefix(const char* unit, r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue); return 0; } if (l == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue); return 0; } diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index ace3c5d2e..373ff789a 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -38,7 +38,14 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); } - if (in_addr_is_null(v->remote_family, &v->remote) == 0) { + if (in_addr_is_null(v->group_family, &v->group) == 0) { + if (v->group_family == AF_INET) + r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in); + 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"); + } else if (in_addr_is_null(v->remote_family, &v->remote) == 0) { if (v->remote_family == AF_INET) r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in); else @@ -189,7 +196,7 @@ int config_parse_vxlan_address(const char *unit, r = in_addr_from_string_auto(rvalue, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -197,14 +204,14 @@ int config_parse_vxlan_address(const char *unit, if (streq(lvalue, "Group")) { if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s invalid multicast address, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s invalid multicast address, ignoring assignment: %s", lvalue, rvalue); return 0; } v->group_family = f; } else { if (r > 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -240,7 +247,7 @@ int config_parse_port_range(const char *unit, r = parse_ip_port_range(rvalue, &low, &high); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue); return 0; } @@ -272,12 +279,12 @@ int config_parse_flow_label(const char *unit, r = safe_atou(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue); return 0; } if (f & ~VXLAN_FLOW_LABEL_MAX_MASK) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "VXLAN flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue); return 0; } @@ -311,13 +318,13 @@ int config_parse_vxlan_ttl(const char *unit, else { r = safe_atou(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN TTL '%s', ignoring assignment: %m", rvalue); return 0; } if (f > 255) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid VXLAN TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue); return 0; } @@ -348,6 +355,11 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { if (!v->dest_port && v->generic_protocol_extension) v->dest_port = 4790; + if (in_addr_is_null(v->group_family, &v->group) == 0 && in_addr_is_null(v->remote_family, &v->remote) == 0) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: VXLAN both 'Group=' and 'Remote=' cannot be specified. Ignoring.", + filename); + return 0; } diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 713cdaa88..6812b07bf 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -348,14 +348,7 @@ static int wireguard_resolve_handler(sd_resolve_query *q, if (ret != 0) { log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret)); - r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL); - if (r < 0) { - log_oom(); - peer->section->invalid = true; - goto resolve_next; - } - - r = set_put(w->peers_with_failed_endpoint, peer); + r = set_ensure_put(&w->peers_with_failed_endpoint, NULL, peer); if (r < 0) { log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m"); peer->section->invalid = true; @@ -466,7 +459,7 @@ int config_parse_wireguard_listen_port( r = parse_ip_port(rvalue, s); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue); return 0; } @@ -501,10 +494,10 @@ static int wireguard_decode_key_and_warn( r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, + return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue); if (len != WG_KEY_LEN) - return log_syntax(unit, LOG_ERR, filename, line, 0, + return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL), "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.", lvalue, len); @@ -580,7 +573,7 @@ int config_parse_wireguard_preshared_key( void *data, void *userdata) { - _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; + WireguardPeer *peer; Wireguard *w; int r; @@ -590,13 +583,9 @@ int config_parse_wireguard_preshared_key( r = wireguard_peer_new_static(w, filename, section_line, &peer); if (r < 0) - return r; + return log_oom(); - r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue); - if (r < 0) - return r; - - TAKE_PTR(peer); + (void) wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue); return 0; } @@ -623,7 +612,7 @@ int config_parse_wireguard_preshared_key_file( r = wireguard_peer_new_static(w, filename, section_line, &peer); if (r < 0) - return r; + return log_oom(); if (isempty(rvalue)) { peer->preshared_key_file = mfree(peer->preshared_key_file); @@ -665,11 +654,11 @@ int config_parse_wireguard_public_key( r = wireguard_peer_new_static(w, filename, section_line, &peer); if (r < 0) - return r; + return log_oom(); r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue); if (r < 0) - return r; + return 0; TAKE_PTR(peer); return 0; @@ -702,25 +691,25 @@ int config_parse_wireguard_allowed_ips( r = wireguard_peer_new_static(w, filename, section_line, &peer); if (r < 0) - return r; + return log_oom(); - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *word = NULL; - r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0); + r = extract_first_word(&p, &word, "," WHITESPACE, 0); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue); break; } r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Network address is invalid, ignoring assignment: %s", word); continue; } @@ -766,15 +755,11 @@ int config_parse_wireguard_endpoint( w = WIREGUARD(data); assert(w); - r = wireguard_peer_new_static(w, filename, section_line, &peer); - if (r < 0) - return r; - if (rvalue[0] == '[') { begin = &rvalue[1]; end = strchr(rvalue, ']'); if (!end) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue); return 0; @@ -782,7 +767,7 @@ int config_parse_wireguard_endpoint( len = end - begin; ++end; if (*end != ':' || !*(end + 1)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unable to find port of endpoint, ignoring assignment: %s", rvalue); return 0; @@ -792,7 +777,7 @@ int config_parse_wireguard_endpoint( begin = rvalue; end = strrchr(rvalue, ':'); if (!end || !*(end + 1)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unable to find port of endpoint, ignoring assignment: %s", rvalue); return 0; @@ -801,6 +786,10 @@ int config_parse_wireguard_endpoint( ++end; } + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return log_oom(); + r = free_and_strndup(&peer->endpoint_host, begin, len); if (r < 0) return log_oom(); @@ -809,15 +798,11 @@ int config_parse_wireguard_endpoint( if (r < 0) return log_oom(); - r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL); + r = set_ensure_put(&w->peers_with_unresolved_endpoint, NULL, peer); if (r < 0) return log_oom(); + TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */ - r = set_put(w->peers_with_unresolved_endpoint, peer); - if (r < 0) - return r; - - TAKE_PTR(peer); return 0; } @@ -833,7 +818,7 @@ int config_parse_wireguard_keepalive( void *data, void *userdata) { - _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; + WireguardPeer *peer; uint16_t keepalive = 0; Wireguard *w; int r; @@ -846,23 +831,21 @@ int config_parse_wireguard_keepalive( r = wireguard_peer_new_static(w, filename, section_line, &peer); if (r < 0) - return r; + return log_oom(); if (streq(rvalue, "off")) keepalive = 0; else { r = safe_atou16(rvalue, &keepalive); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m", rvalue); return 0; } } peer->persistent_keepalive_interval = keepalive; - - TAKE_PTR(peer); return 0; } @@ -905,7 +888,10 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_ (void) warn_file_is_world_accessible(filename, NULL, NULL, 0); - r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len); + r = read_full_file_full( + AT_FDCWD, filename, + READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, + &key, &key_len); if (r < 0) return r; diff --git a/src/network/networkctl.c b/src/network/networkctl.c index a9390b868..48182e61d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include #include @@ -7,18 +8,23 @@ #include #include #include +#include +#include #include "sd-bus.h" #include "sd-device.h" +#include "sd-dhcp-client.h" #include "sd-hwdb.h" #include "sd-lldp.h" #include "sd-netlink.h" #include "sd-network.h" #include "alloc-util.h" +#include "bond-util.h" +#include "bridge-util.h" #include "bus-common-errors.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-locator.h" #include "device-util.h" #include "escape.h" #include "ether-addr-util.h" @@ -26,12 +32,15 @@ #include "fd-util.h" #include "format-table.h" #include "format-util.h" +#include "geneve-util.h" #include "glob-util.h" #include "hwdb-util.h" +#include "ipvlan-util.h" #include "local-addresses.h" #include "locale-util.h" #include "logs-show.h" #include "macro.h" +#include "macvlan-util.h" #include "main-func.h" #include "netlink-util.h" #include "network-internal.h" @@ -110,6 +119,14 @@ typedef struct VxLanInfo { uint16_t dest_port; + uint8_t proxy; + uint8_t learning; + uint8_t inerit; + uint8_t rsc; + uint8_t l2miss; + uint8_t l3miss; + uint8_t tos; + uint8_t ttl; } VxLanInfo; typedef struct LinkInfo { @@ -120,11 +137,14 @@ typedef struct LinkInfo { unsigned short iftype; struct ether_addr mac_address; struct ether_addr permanent_mac_address; + uint32_t master; uint32_t mtu; uint32_t min_mtu; uint32_t max_mtu; uint32_t tx_queues; uint32_t rx_queues; + uint8_t addr_gen_mode; + char *qdisc; char **alternative_names; union { @@ -141,12 +161,44 @@ typedef struct LinkInfo { uint32_t max_age; uint32_t ageing_time; uint32_t stp_state; + uint32_t cost; uint16_t priority; uint8_t mcast_igmp_version; + uint8_t port_state; /* vxlan info */ VxLanInfo vxlan_info; + /* vlan info */ + uint16_t vlan_id; + + /* tunnel info */ + uint8_t ttl; + uint8_t tos; + uint8_t inherit; + uint8_t df; + uint8_t csum; + uint8_t csum6_tx; + uint8_t csum6_rx; + uint16_t tunnel_port; + uint32_t vni; + uint32_t label; + union in_addr_union local; + union in_addr_union remote; + + /* bonding info */ + uint8_t mode; + uint32_t miimon; + uint32_t updelay; + uint32_t downdelay; + + /* macvlan and macvtap info */ + uint32_t macvlan_mode; + + /* ipvlan info */ + uint16_t ipvlan_mode; + uint16_t ipvlan_flags; + /* ethtool info */ int autonegotiation; uint64_t speed; @@ -167,6 +219,8 @@ typedef struct LinkInfo { bool has_bitrates:1; bool has_ethtool_link_info:1; bool has_wlan_link_info:1; + bool has_tunnel_ipv4:1; + bool has_ipv6_address_generation_mode:1; bool needs_freeing:1; } LinkInfo; @@ -179,6 +233,7 @@ static const 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].ssid); + free(array[i].qdisc); strv_free(array[i].alternative_names); } @@ -211,9 +266,15 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age); (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time); (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state); + (void) sd_netlink_message_read_u32(m, IFLA_BRPORT_COST, &info->cost); (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")) { + (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")) { (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni); @@ -237,6 +298,53 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link); (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port); + (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_PROXY, &info->vxlan_info.proxy); + (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_LEARNING, &info->vxlan_info.learning); + (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_RSC, &info->vxlan_info.rsc); + (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L3MISS, &info->vxlan_info.l3miss); + (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")) + (void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id); + else if (STR_IN_SET(received_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")) { + (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); + if (r >= 0) + info->has_tunnel_ipv4 = true; + else + (void) sd_netlink_message_read_in6_addr(m, IFLA_GENEVE_REMOTE6, &info->remote.in6); + + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL, &info->ttl); + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL_INHERIT, &info->inherit); + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TOS, &info->tos); + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_DF, &info->df); + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_CSUM, &info->csum); + (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, &info->csum6_tx); + (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")) { + (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")) { + (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")) { + (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")) { + (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")) + (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode); + else if (streq(received_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); @@ -249,7 +357,7 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) { _cleanup_strv_free_ char **altnames = NULL; - const char *name; + const char *name, *qdisc; int ifindex, r; uint16_t type; @@ -272,7 +380,7 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b return r; r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames); - if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENODATA)) + if (r < 0 && r != -ENODATA) return r; if (patterns) { @@ -333,15 +441,45 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0) info->has_stats = true; + r = sd_netlink_message_read_string(m, IFLA_QDISC, &qdisc); + if (r >= 0) { + info->qdisc = strdup(qdisc); + if (!info->qdisc) + return log_oom(); + } + + (void) sd_netlink_message_read_u32(m, IFLA_MASTER, &info->master); + + r = sd_netlink_message_enter_container(m, IFLA_AF_SPEC); + if (r >= 0) { + r = sd_netlink_message_enter_container(m, AF_INET6); + if (r >= 0) { + r = sd_netlink_message_read_u8(m, IFLA_INET6_ADDR_GEN_MODE, &info->addr_gen_mode); + if (r >= 0 && IN_SET(info->addr_gen_mode, + IN6_ADDR_GEN_MODE_EUI64, + IN6_ADDR_GEN_MODE_NONE, + IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM)) + info->has_ipv6_address_generation_mode = true; + + (void) sd_netlink_message_exit_container(m); + } + (void) sd_netlink_message_exit_container(m); + } + /* fill kind info */ (void) decode_netdev(m, info); return 1; } -static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +static int link_get_property( + sd_bus *bus, + const LinkInfo *link, + sd_bus_error *error, + sd_bus_message **reply, + const char *iface, + const char *propname) { _cleanup_free_ char *path = NULL, *ifindex_str = NULL; int r; @@ -352,17 +490,25 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { if (r < 0) return r; - r = sd_bus_call_method( + return sd_bus_call_method( bus, "org.freedesktop.network1", path, "org.freedesktop.DBus.Properties", "Get", - &error, - &reply, + error, + reply, "ss", - "org.freedesktop.network1.Link", - "BitRates"); + iface, + propname); +} + +static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.Link", "BitRates"); if (r < 0) { bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) || sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE); @@ -573,7 +719,7 @@ static int list_links(int argc, char *argv[], void *userdata) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); if (arg_legend) printf("\n%i links listed.\n", c); @@ -785,12 +931,13 @@ static int dump_gateways( static int dump_addresses( sd_netlink *rtnl, + sd_dhcp_lease *lease, Table *table, int ifindex) { _cleanup_free_ struct local_address *local = NULL; - _cleanup_free_ char *dhcp4_address = NULL; _cleanup_strv_free_ char **buf = NULL; + struct in_addr dhcp4_address = {}; int r, n, i; assert(rtnl); @@ -800,7 +947,8 @@ static int dump_addresses( if (n <= 0) return n; - (void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address); + if (lease) + (void) sd_dhcp_lease_get_address(lease, &dhcp4_address); for (i = 0; i < n; i++) { _cleanup_free_ char *pretty = NULL; @@ -810,13 +958,19 @@ static int dump_addresses( if (r < 0) return r; - if (dhcp4_address && streq(pretty, dhcp4_address)) { - _cleanup_free_ char *p = NULL; + if (local[i].family == AF_INET && in4_addr_equal(&local[i].address.in, &dhcp4_address)) { + struct in_addr server_address; + char *p, s[INET_ADDRSTRLEN]; - p = pretty; - pretty = strjoin(pretty , " (DHCP4)"); - if (!pretty) + r = sd_dhcp_lease_get_server_identifier(lease, &server_address); + if (r >= 0 && inet_ntop(AF_INET, &server_address, s, sizeof(s))) + p = strjoin(pretty, " (DHCP4 via ", s, ")"); + else + p = strjoin(pretty, " (DHCP4)"); + if (!p) return log_oom(); + + free_and_replace(pretty, p); } r = strv_extendf(&buf, "%s%s%s", @@ -910,7 +1064,7 @@ static int dump_address_labels(sd_netlink *rtnl) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); return 0; } @@ -1018,6 +1172,96 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) { return dump_list(table, prefix, buf); } +static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) { + _cleanup_strv_free_ char **buf = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases"); + if (r < 0) { + bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY); + + log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, + r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r)); + return 0; + } + + r = sd_bus_message_enter_container(reply, 'v', "a(uayayayayt)"); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) { + _cleanup_free_ char *id = NULL, *ip = NULL; + const void *client_id, *addr, *gtw, *hwaddr; + size_t client_id_sz, sz; + uint64_t expiration; + uint32_t family; + + r = sd_bus_message_read(reply, "u", &family); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &addr, &sz); + if (r < 0 || sz != 4) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', >w, &sz); + if (r < 0 || sz != 4) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_basic(reply, 't', &expiration); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id); + if (r < 0) + return bus_log_parse_error(r); + + r = in_addr_to_string(family, addr, &ip); + if (r < 0) + return bus_log_parse_error(r); + + r = strv_extendf(&buf, "%s (to %s)", ip, id); + if (r < 0) + return log_oom(); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (strv_isempty(buf)) { + r = strv_extendf(&buf, "none"); + if (r < 0) + return log_oom(); + } + + return dump_list(table, prefix, buf); +} + static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) { unsigned c; int r; @@ -1138,17 +1382,18 @@ static int show_logs(const LinkInfo *info) { } static int link_status_one( + sd_bus *bus, sd_netlink *rtnl, sd_hwdb *hwdb, const LinkInfo *info) { - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; - _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; - _cleanup_free_ char *t = NULL, *network = NULL; - const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL; - const char *on_color_operational, *off_color_operational, - *on_color_setup, *off_color_setup; + _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, *lease_file = 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; _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; + _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_(table_unrefp) Table *table = NULL; TableCell *cell; int r; @@ -1168,6 +1413,7 @@ static int link_status_one( (void) sd_network_link_get_search_domains(info->ifindex, &search_domains); (void) sd_network_link_get_route_domains(info->ifindex, &route_domains); (void) sd_network_link_get_ntp(info->ifindex, &ntp); + (void) sd_network_link_get_sip(info->ifindex, &sip); if (info->sd_device) { (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link); @@ -1188,6 +1434,11 @@ 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(); + + (void) dhcp_lease_load(&lease, lease_file); + table = table_new("dot", "key", "value"); if (!table) return log_oom(); @@ -1235,6 +1486,7 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); + strv_sort(info->alternative_names); r = dump_list(table, "Alternative Names:", info->alternative_names); if (r < 0) return r; @@ -1336,6 +1588,42 @@ static int link_status_one( return table_log_add_error(r); } + if (info->qdisc) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "QDisc:", + TABLE_STRING, info->qdisc); + if (r < 0) + return table_log_add_error(r); + } + + if (info->master > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Master:", + TABLE_IFINDEX, info->master); + if (r < 0) + return table_log_add_error(r); + } + + if (info->has_ipv6_address_generation_mode) { + static const struct { + const char *mode; + } mode_table[] = { + { "eui64" }, + { "none" }, + { "stable-privacy" }, + { "random" }, + }; + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "IPv6 Address Generation Mode:", + TABLE_STRING, mode_table[info->addr_gen_mode]); + if (r < 0) + return table_log_add_error(r); + } + if (streq_ptr(info->netdev_kind, "bridge")) { r = table_add_many(table, TABLE_EMPTY, @@ -1358,11 +1646,39 @@ static int link_status_one( TABLE_BOOLEAN, info->stp_state > 0, TABLE_EMPTY, TABLE_STRING, "Multicast IGMP Version:", - TABLE_UINT8, info->mcast_igmp_version); + TABLE_UINT8, info->mcast_igmp_version, + TABLE_EMPTY, + TABLE_STRING, "Cost:", + TABLE_UINT32, info->cost); + if (r < 0) + return table_log_add_error(r); + + if (info->port_state <= BR_STATE_BLOCKING) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Port State:", + TABLE_STRING, bridge_state_to_string(info->port_state)); + } + } else if (streq_ptr(info->netdev_kind, "bond")) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Mode:", + TABLE_STRING, bond_mode_to_string(info->mode), + TABLE_EMPTY, + TABLE_STRING, "Miimon:", + TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->miimon), + TABLE_EMPTY, + TABLE_STRING, "Updelay:", + TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->updelay), + TABLE_EMPTY, + TABLE_STRING, "Downdelay:", + TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->downdelay)); if (r < 0) return table_log_add_error(r); } else if (streq_ptr(info->netdev_kind, "vxlan")) { + char ttl[CONST_MAX(STRLEN("auto") + 1, DECIMAL_STR_MAX(uint8_t))]; + if (info->vxlan_info.vni > 0) { r = table_add_many(table, TABLE_EMPTY, @@ -1373,9 +1689,17 @@ static int link_status_one( } if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) { + const char *p; + + r = in_addr_is_multicast(info->vxlan_info.group_family, &info->vxlan_info.group); + if (r <= 0) + p = "Remote:"; + else + p = "Group:"; + r = table_add_many(table, TABLE_EMPTY, - TABLE_STRING, "Group:", + TABLE_STRING, p, info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR, &info->vxlan_info.group); if (r < 0) @@ -1409,6 +1733,221 @@ static int link_status_one( if (r < 0) return table_log_add_error(r); } + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Learning:", + TABLE_BOOLEAN, info->vxlan_info.learning); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "RSC:", + TABLE_BOOLEAN, info->vxlan_info.rsc); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "L3MISS:", + TABLE_BOOLEAN, info->vxlan_info.l3miss); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "L2MISS:", + TABLE_BOOLEAN, info->vxlan_info.l2miss); + if (r < 0) + return table_log_add_error(r); + + if (info->vxlan_info.tos > 1) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "TOS:", + TABLE_UINT8, info->vxlan_info.tos); + if (r < 0) + return table_log_add_error(r); + } + + if (info->vxlan_info.ttl > 0) + xsprintf(ttl, "%" PRIu8, info->vxlan_info.ttl); + else + strcpy(ttl, "auto"); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "TTL:", + TABLE_STRING, ttl); + if (r < 0) + return table_log_add_error(r); + } else if (streq_ptr(info->netdev_kind, "vlan") && info->vlan_id > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "VLan Id:", + TABLE_UINT16, info->vlan_id); + if (r < 0) + return table_log_add_error(r); + } else if (STRPTR_IN_SET(info->netdev_kind, "ipip", "sit", "gre", "gretap", "erspan", "vti")) { + if (!in_addr_is_null(AF_INET, &info->local)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Local:", + TABLE_IN_ADDR, &info->local); + if (r < 0) + return table_log_add_error(r); + } + + if (!in_addr_is_null(AF_INET, &info->remote)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Remote:", + TABLE_IN_ADDR, &info->remote); + if (r < 0) + return table_log_add_error(r); + } + } else if (STRPTR_IN_SET(info->netdev_kind, "ip6gre", "ip6gretap", "ip6erspan", "vti6")) { + if (!in_addr_is_null(AF_INET6, &info->local)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Local:", + TABLE_IN6_ADDR, &info->local); + if (r < 0) + return table_log_add_error(r); + } + + if (!in_addr_is_null(AF_INET6, &info->remote)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Remote:", + TABLE_IN6_ADDR, &info->remote); + if (r < 0) + return table_log_add_error(r); + } + } else if (streq_ptr(info->netdev_kind, "geneve")) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "VNI:", + TABLE_UINT32, info->vni); + if (r < 0) + return table_log_add_error(r); + + if (info->has_tunnel_ipv4 && !in_addr_is_null(AF_INET, &info->remote)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Remote:", + TABLE_IN_ADDR, &info->remote); + if (r < 0) + return table_log_add_error(r); + } else if (!in_addr_is_null(AF_INET6, &info->remote)) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Remote:", + TABLE_IN6_ADDR, &info->remote); + if (r < 0) + return table_log_add_error(r); + } + + if (info->ttl > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "TTL:", + TABLE_UINT8, info->ttl); + if (r < 0) + return table_log_add_error(r); + } + + if (info->tos > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "TOS:", + TABLE_UINT8, info->tos); + if (r < 0) + return table_log_add_error(r); + } + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Port:", + TABLE_UINT16, info->tunnel_port); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Inherit:", + TABLE_STRING, geneve_df_to_string(info->inherit)); + if (r < 0) + return table_log_add_error(r); + + if (info->df > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "IPDoNotFragment:", + TABLE_UINT8, info->df); + if (r < 0) + return table_log_add_error(r); + } + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "UDPChecksum:", + TABLE_BOOLEAN, info->csum); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "UDP6ZeroChecksumTx:", + TABLE_BOOLEAN, info->csum6_tx); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "UDP6ZeroChecksumRx:", + TABLE_BOOLEAN, info->csum6_rx); + if (r < 0) + return table_log_add_error(r); + + if (info->label > 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "FlowLabel:", + TABLE_UINT32, info->label); + if (r < 0) + return table_log_add_error(r); + } + } else if (STRPTR_IN_SET(info->netdev_kind, "macvlan", "macvtap")) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Mode:", + TABLE_STRING, macvlan_mode_to_string(info->macvlan_mode)); + if (r < 0) + return table_log_add_error(r); + } else if (streq_ptr(info->netdev_kind, "ipvlan")) { + _cleanup_free_ char *p = NULL, *s = NULL; + + if (info->ipvlan_flags & IPVLAN_F_PRIVATE) + p = strdup("private"); + else if (info->ipvlan_flags & IPVLAN_F_VEPA) + p = strdup("vepa"); + else + p = strdup("bridge"); + if (!p) + log_oom(); + + s = strjoin(ipvlan_mode_to_string(info->ipvlan_mode), " (", p, ")"); + if (!s) + return log_oom(); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Mode:", + TABLE_STRING, s); + if (r < 0) + return table_log_add_error(r); } if (info->has_wlan_link_info) { @@ -1498,7 +2037,7 @@ static int link_status_one( } } - r = dump_addresses(rtnl, table, info->ifindex); + r = dump_addresses(rtnl, lease, table, info->ifindex); if (r < 0) return r; r = dump_gateways(rtnl, hwdb, table, info->ifindex); @@ -1514,6 +2053,9 @@ static int link_status_one( if (r < 0) return r; r = dump_list(table, "NTP:", ntp); + if (r < 0) + return r; + r = dump_list(table, "SIP:", sip); if (r < 0) return r; r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to); @@ -1523,12 +2065,53 @@ static int link_status_one( if (r < 0) return r; - (void) sd_network_link_get_timezone(info->ifindex, &tz); - if (tz) { + if (lease) { + const void *client_id; + size_t client_id_len; + const char *tz; + + r = sd_dhcp_lease_get_timezone(lease, &tz); + if (r >= 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "Time Zone:", + TABLE_STRING, tz); + if (r < 0) + return table_log_add_error(r); + } + + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); + if (r >= 0) { + _cleanup_free_ char *id = NULL; + + r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id); + if (r >= 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "DHCP4 Client ID:", + TABLE_STRING, id); + if (r < 0) + return table_log_add_error(r); + } + } + } + + r = sd_network_link_get_dhcp6_client_iaid_string(info->ifindex, &iaid); + if (r >= 0) { r = table_add_many(table, TABLE_EMPTY, - TABLE_STRING, "Time Zone:", - TABLE_STRING, tz); + TABLE_STRING, "DHCP6 Client IAID:", + TABLE_STRING, iaid); + if (r < 0) + return table_log_add_error(r); + } + + r = sd_network_link_get_dhcp6_client_duid_string(info->ifindex, &duid); + if (r >= 0) { + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "DHCP6 Client DUID:", + TABLE_STRING, duid); if (r < 0) return table_log_add_error(r); } @@ -1537,13 +2120,17 @@ static int link_status_one( if (r < 0) return r; + r = dump_dhcp_leases(table, "Offered DHCP leases:", bus, info); + if (r < 0) + return r; + r = dump_statistics(table, info); if (r < 0) return r; r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); return show_logs(info); } @@ -1586,7 +2173,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { if (r < 0) return table_log_add_error(r); - r = dump_addresses(rtnl, table, 0); + r = dump_addresses(rtnl, NULL, table, 0); if (r < 0) return r; r = dump_gateways(rtnl, hwdb, table, 0); @@ -1615,7 +2202,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); return show_logs(NULL); } @@ -1654,7 +2241,7 @@ static int link_status(int argc, char *argv[], void *userdata) { if (i > 0) fputc('\n', stdout); - link_status_one(rtnl, hwdb, links + i); + link_status_one(bus, rtnl, hwdb, links + i); } return 0; @@ -1835,7 +2422,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); if (arg_legend) { lldp_capabilities_legend(all); @@ -1862,6 +2449,69 @@ static int link_delete_send_message(sd_netlink *rtnl, int index) { return 0; } +static int link_up_down_send_message(sd_netlink *rtnl, char *command, int index) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(rtnl); + + r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, index); + if (r < 0) + return rtnl_log_create_error(r); + + if (streq(command, "up")) + r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); + else + r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); + if (r < 0) + return log_error_errno(r, "Could not set link flags: %m"); + + r = sd_netlink_call(rtnl, req, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +static int link_up_down(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_set_free_ Set *indexes = NULL; + int index, r, i; + Iterator j; + void *p; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + indexes = set_new(NULL); + if (!indexes) + return log_oom(); + + for (i = 1; i < argc; i++) { + index = resolve_interface_or_warn(&rtnl, argv[i]); + if (index < 0) + return index; + + r = set_put(indexes, INT_TO_PTR(index)); + if (r < 0) + return log_oom(); + } + + SET_FOREACH(p, indexes, j) { + index = PTR_TO_INT(p); + r = link_up_down_send_message(rtnl, argv[0], index); + if (r < 0) { + char ifname[IF_NAMESIZE + 1]; + + return log_error_errno(r, "Failed to %s interface %s: %m", + argv[1], format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX)); + } + } + + return r; +} + static int link_delete(int argc, char *argv[], void *userdata) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_set_free_ Set *indexes = NULL; @@ -1905,15 +2555,7 @@ static int link_renew_one(sd_bus *bus, int index, const char *name) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "RenewLink", - &error, - NULL, - "i", index); + r = bus_call_method(bus, bus_network_mgr, "RenewLink", &error, NULL, "i", index); if (r < 0) return log_error_errno(r, "Failed to renew dynamic configuration of interface %s: %s", name, bus_error_message(&error, r)); @@ -1943,6 +2585,40 @@ static int link_renew(int argc, char *argv[], void *userdata) { return k; } +static int link_force_renew_one(sd_bus *bus, int index, const char *name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = bus_call_method(bus, bus_network_mgr, "ForceRenewLink", &error, NULL, "i", index); + if (r < 0) + return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s", + name, bus_error_message(&error, r)); + + return 0; +} + +static int link_force_renew(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int index, i, k = 0, r; + + r = sd_bus_open_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to connect system bus: %m"); + + for (i = 1; i < argc; i++) { + index = resolve_interface_or_warn(&rtnl, argv[i]); + if (index < 0) + return index; + + r = link_force_renew_one(bus, index, argv[i]); + if (r < 0 && k >= 0) + k = r; + } + + return k; +} + static int verb_reload(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; @@ -1952,13 +2628,7 @@ static int verb_reload(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to connect system bus: %m"); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "Reload", - &error, NULL, NULL); + 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"); @@ -1994,13 +2664,7 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) { SET_FOREACH(p, indexes, j) { index = PTR_TO_INT(p); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "ReconfigureLink", - &error, NULL, "i", index); + r = bus_call_method(bus, bus_network_mgr, "ReconfigureLink", &error, NULL, "i", index); if (r < 0) { char ifname[IF_NAMESIZE + 1]; @@ -2028,7 +2692,10 @@ static int help(void) { " lldp [PATTERN...] Show LLDP neighbors\n" " label Show current address label entries in the kernel\n" " delete DEVICES... Delete virtual netdevs\n" + " up DEVICES... Bring devices up\n" + " down DEVICES... Bring devices down\n" " renew DEVICES... Renew dynamic configurations\n" + " forcerenew DEVICES... Trigger DHCP reconfiguration of all connected clients\n" " reconfigure DEVICES... Reconfigure interfaces\n" " reload Reload .network and .netdev files\n" "\nOptions:\n" @@ -2129,7 +2796,10 @@ static int networkctl_main(int argc, char *argv[]) { { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status }, { "label", VERB_ANY, VERB_ANY, 0, list_address_labels }, { "delete", 2, VERB_ANY, 0, link_delete }, + { "up", 2, VERB_ANY, 0, link_up_down }, + { "down", 2, VERB_ANY, 0, link_up_down }, { "renew", 2, VERB_ANY, 0, link_renew }, + { "forcerenew", 2, VERB_ANY, 0, link_force_renew }, { "reconfigure", 2, VERB_ANY, 0, verb_reconfigure }, { "reload", 1, 1, 0, verb_reload }, {} @@ -2149,9 +2819,7 @@ static void warn_networkd_missing(void) { static int run(int argc, char* argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 32b79dd15..0d53aa904 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -173,11 +173,11 @@ int config_parse_address_label_prefix(const char *unit, r = address_label_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = in_addr_prefix_from_string(rvalue, AF_INET6, &n->in_addr, &n->prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Address label is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -211,16 +211,16 @@ int config_parse_address_label( r = address_label_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = safe_atou32(rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue); return 0; } if (k == 0xffffffffUL) { - log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue); return 0; } diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index c61e517ea..b09d75e61 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -20,6 +20,24 @@ #define ADDRESSES_PER_LINK_MAX 2048U #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U +int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) { + assert(link); + assert(ret); + + /* see RFC4291 section 2.5.1 */ + ret->s6_addr[8] = link->mac.ether_addr_octet[0]; + ret->s6_addr[8] ^= 1 << 1; + ret->s6_addr[9] = link->mac.ether_addr_octet[1]; + ret->s6_addr[10] = link->mac.ether_addr_octet[2]; + ret->s6_addr[11] = 0xff; + ret->s6_addr[12] = 0xfe; + ret->s6_addr[13] = link->mac.ether_addr_octet[3]; + ret->s6_addr[14] = link->mac.ether_addr_octet[4]; + ret->s6_addr[15] = link->mac.ether_addr_octet[5]; + + return 0; +} + int address_new(Address **ret) { _cleanup_(address_freep) Address *address = NULL; @@ -107,6 +125,17 @@ void address_free(Address *address) { if (address->link && !address->acd) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + set_remove(address->link->static_addresses, address); + if (address->link->dhcp_address == address) + address->link->dhcp_address = NULL; + if (address->link->dhcp_address_old == address) + address->link->dhcp_address_old = NULL; + set_remove(address->link->dhcp6_addresses, address); + set_remove(address->link->dhcp6_addresses_old, address); + set_remove(address->link->dhcp6_pd_addresses, address); + set_remove(address->link->dhcp6_pd_addresses_old, address); + set_remove(address->link->ndisc_addresses, address); + set_remove(address->link->ndisc_addresses_old, address); if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); @@ -187,7 +216,7 @@ static int address_compare_func(const Address *a1, const Address *a2) { } } -DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free); bool address_equal(Address *a1, Address *a2) { if (a1 == a2) @@ -248,11 +277,7 @@ static int address_add_internal(Link *link, Set **addresses, /* Consider address tentative until we get the real flags from the kernel */ address->flags = IFA_F_TENTATIVE; - r = set_ensure_allocated(addresses, &address_hash_ops); - if (r < 0) - return r; - - r = set_put(*addresses, address); + r = set_ensure_put(addresses, &address_hash_ops, address); if (r < 0) return r; if (r == 0) @@ -262,9 +287,7 @@ static int address_add_internal(Link *link, Set **addresses, if (ret) *ret = address; - - address = NULL; - + TAKE_PTR(address); return 0; } @@ -284,11 +307,7 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi return r; } else if (r == 0) { /* Take over a foreign address */ - r = set_ensure_allocated(&link->addresses, &address_hash_ops); - if (r < 0) - return r; - - r = set_put(link->addresses, address); + r = set_ensure_put(&link->addresses, &address_hash_ops, address); if (r < 0) return r; @@ -336,11 +355,8 @@ int address_update( int r; assert(address); + assert(address->link); assert(cinfo); - assert_return(address->link, 1); - - if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; ready = address_is_ready(address); @@ -348,18 +364,27 @@ int address_update( address->scope = scope; address->cinfo = *cinfo; + if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + link_update_operstate(address->link, true); link_check_ready(address->link); - if (!ready && - address_is_ready(address) && - address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && - in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + if (!ready && address_is_ready(address)) { + if (address->callback) { + r = address->callback(address); + if (r < 0) + return r; + } - r = link_ipv6ll_gained(address->link, &address->in_addr.in6); - if (r < 0) - return r; + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + IN6_IS_ADDR_UNSPECIFIED(&address->link->ipv6ll_address) > 0) { + + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); + if (r < 0) + return r; + } } return 0; @@ -423,6 +448,32 @@ int address_get(Link *link, return -ENOENT; } +static bool address_exists_internal(Set *addresses, int family, const union in_addr_union *in_addr) { + Address *address; + Iterator i; + + SET_FOREACH(address, addresses, i) { + if (address->family != family) + continue; + if (in_addr_equal(address->family, &address->in_addr, in_addr)) + return true; + } + + return false; +} + +bool address_exists(Link *link, int family, const union in_addr_union *in_addr) { + assert(link); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(in_addr); + + if (address_exists_internal(link->addresses, family, in_addr)) + return true; + if (address_exists_internal(link->addresses_foreign, family, in_addr)) + return true; + return false; +} + static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -436,6 +487,8 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EADDRNOTAVAIL) log_link_message_warning_errno(link, m, r, "Could not drop address"); + else + (void) manager_rtnl_process_address(rtnl, m, link->manager); return 1; } @@ -550,9 +603,11 @@ int address_configure( Address *address, Link *link, link_netlink_message_handler_t callback, - bool update) { + bool update, + Address **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + Address *a; int r; assert(address); @@ -573,6 +628,13 @@ int address_configure( if (r < 0) return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m"); + if (DEBUG_LOGGING) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(address->family, &address->in_addr, &str); + log_link_debug(link, "%s address: %s", update ? "Updating" : "Configuring", strna(str)); + } + if (update) r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, link->ifindex, address->family); @@ -654,9 +716,9 @@ int address_configure( link_ref(link); if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer)) - r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, NULL); + r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a); else - r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); + r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a); if (r < 0) { address_release(address); return log_link_error_errno(link, r, "Could not add address: %m"); @@ -676,6 +738,9 @@ int address_configure( log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); } + if (ret) + *ret = a; + return 1; } @@ -780,18 +845,23 @@ int config_parse_broadcast( assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } if (n->family == AF_INET6) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue); return 0; } r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -831,14 +901,18 @@ int config_parse_address(const char *unit, r = address_new_static(network, NULL, 0, &n); } else r = address_new_static(network, filename, section_line, &n); - - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } /* Address=address/prefixlen */ r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_REFUSE, &f, &buffer, &prefixlen); if (r == -ENOANO) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "An address '%s' is specified without prefix length. " "The behavior of parsing addresses without prefix length will be changed in the future release. " "Please specify prefix length explicitly.", rvalue); @@ -846,12 +920,12 @@ int config_parse_address(const char *unit, r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_LEGACY, &f, &buffer, &prefixlen); } if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue); return 0; } if (n->family != AF_UNSPEC && f != n->family) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Address is incompatible, ignoring assignment: %s", rvalue); return 0; } @@ -861,7 +935,7 @@ int config_parse_address(const char *unit, * let's limit the prefix length to 64 or larger. See RFC4193. */ if ((f == AF_INET && prefixlen < 8) || (f == AF_INET6 && prefixlen < 64)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Null address with invalid prefixlen='%u', ignoring assignment: %s", prefixlen, rvalue); return 0; @@ -907,11 +981,16 @@ int config_parse_label( assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } if (!address_label_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface label is too long or invalid, ignoring assignment: %s", rvalue); return 0; } @@ -936,7 +1015,7 @@ int config_parse_lifetime(const char *unit, void *userdata) { Network *network = userdata; _cleanup_(address_free_or_set_invalidp) Address *n = NULL; - unsigned k; + uint32_t k; int r; assert(filename); @@ -946,8 +1025,13 @@ int config_parse_lifetime(const char *unit, assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } /* We accept only "forever", "infinity", empty, or "0". */ if (STR_IN_SET(rvalue, "forever", "infinity", "")) @@ -955,13 +1039,13 @@ int config_parse_lifetime(const char *unit, else if (streq(rvalue, "0")) k = 0; else { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid PreferredLifetime= value, ignoring: %s", rvalue); return 0; } n->cinfo.ifa_prefered = k; - n = NULL; + TAKE_PTR(n); return 0; } @@ -987,12 +1071,17 @@ int config_parse_address_flags(const char *unit, assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring: %s", lvalue, rvalue); return 0; } @@ -1035,8 +1124,13 @@ int config_parse_address_scope(const char *unit, assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } if (streq(rvalue, "host")) n->scope = RT_SCOPE_HOST; @@ -1047,7 +1141,7 @@ int config_parse_address_scope(const char *unit, else { r = safe_atou8(rvalue , &n->scope); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse address scope \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1081,8 +1175,13 @@ int config_parse_duplicate_address_detection( assert(data); r = address_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate new address, ignoring assignment: %m"); + return 0; + } r = parse_boolean(rvalue); if (r >= 0) { @@ -1097,7 +1196,7 @@ int config_parse_duplicate_address_detection( a = duplicate_address_detection_address_family_from_string(rvalue); if (a < 0) { - log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL), "Failed to parse %s=, ignoring: %s", lvalue, rvalue); return 0; } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index ad2412c75..3fc9935d1 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -20,6 +20,7 @@ typedef struct Address Address; typedef struct Network Network; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; +typedef int (*address_ready_callback_t)(Address *address); struct Address { Network *network; @@ -47,6 +48,9 @@ struct Address { bool autojoin:1; AddressFamily duplicate_address_detection; + /* Called when address become ready */ + address_ready_callback_t callback; + sd_ipv4acd *acd; LIST_FIELDS(Address, addresses); @@ -57,15 +61,18 @@ void address_free(Address *address); int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); +bool address_exists(Link *link, int family, const union in_addr_union *in_addr); int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo); int address_drop(Address *address); -int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update); +int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret); int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback); bool address_equal(Address *a1, Address *a2); bool address_is_ready(const Address *a); int address_section_verify(Address *a); int configure_ipv4_duplicate_address_detection(Link *link, Address *address); +int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret); + DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); extern const struct hash_ops address_hash_ops; diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c index 41f09287f..3fc252d21 100644 --- a/src/network/networkd-brvlan.c +++ b/src/network/networkd-brvlan.c @@ -241,7 +241,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename, r = parse_vid_range(rvalue, &vid, &vid_end); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); return 0; } @@ -269,7 +269,7 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename, r = parse_vid_range(rvalue, &vid, &vid_end); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue); return 0; } diff --git a/src/network/networkd-can.c b/src/network/networkd-can.c index 5087dbbc2..e5504f773 100644 --- a/src/network/networkd-can.c +++ b/src/network/networkd-can.c @@ -7,8 +7,51 @@ #include "networkd-can.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "parse-util.h" #include "string-util.h" +#define CAN_TERMINATION_OHM_VALUE 120 + +int config_parse_can_bitrate( + 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 *br = data; + uint64_t sz; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_size(rvalue, 1000, &sz); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse can bitrate '%s', ignoring: %m", rvalue); + return 0; + } + + /* Linux uses __u32 for bitrates, so the value should not exceed that. */ + if (sz <= 0 || sz > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Bit rate out of permitted range 1...4294967295"); + return 0; + } + + *br = (uint32_t) sz; + + return 0; +} + static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -69,6 +112,7 @@ static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) static int link_set_can(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + struct can_ctrlmode cm = {}; int r; assert(link); @@ -100,11 +144,6 @@ static int link_set_can(Link *link) { .sample_point = link->network->can_sample_point, }; - if (link->network->can_bitrate > UINT32_MAX) { - log_link_error(link, "bitrate (%" PRIu64 ") too big.", link->network->can_bitrate); - return -ERANGE; - } - log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate); if (link->network->can_sample_point > 0) log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); @@ -116,6 +155,35 @@ static int link_set_can(Link *link) { return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); } + if (link->network->can_data_bitrate > 0 || link->network->can_data_sample_point > 0) { + struct can_bittiming bt = { + .bitrate = link->network->can_data_bitrate, + .sample_point = link->network->can_data_sample_point, + }; + + log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate); + if (link->network->can_data_sample_point > 0) + log_link_debug(link, "Setting data sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10); + else + log_link_debug(link, "Using default data sample point"); + + r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m"); + } + + if (link->network->can_fd_mode >= 0) { + cm.mask |= CAN_CTRLMODE_FD; + SET_FLAG(cm.flags, CAN_CTRLMODE_FD, link->network->can_fd_mode > 0); + log_link_debug(link, "%sabling FD mode", link->network->can_fd_mode > 0 ? "En" : "Dis"); + } + + if (link->network->can_non_iso >= 0) { + cm.mask |= CAN_CTRLMODE_FD_NON_ISO; + SET_FLAG(cm.flags, CAN_CTRLMODE_FD_NON_ISO, link->network->can_non_iso > 0); + log_link_debug(link, "%sabling FD non-ISO mode", link->network->can_non_iso > 0 ? "En" : "Dis"); + } + if (link->network->can_restart_us > 0) { char time_string[FORMAT_TIMESPAN_MAX]; uint64_t restart_ms; @@ -140,18 +208,34 @@ static int link_set_can(Link *link) { } if (link->network->can_triple_sampling >= 0) { - struct can_ctrlmode cm = { - .mask = CAN_CTRLMODE_3_SAMPLES, - .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0, - }; - + cm.mask |= CAN_CTRLMODE_3_SAMPLES; + SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling); log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis"); + } + if (link->network->can_listen_only >= 0) { + cm.mask |= CAN_CTRLMODE_LISTENONLY; + SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only); + log_link_debug(link, "%sabling listen-only mode", link->network->can_listen_only ? "En" : "Dis"); + } + + if (cm.mask != 0) { r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); } + if (link->network->can_termination >= 0) { + + log_link_debug(link, "%sabling can-termination", link->network->can_termination ? "En" : "Dis"); + + r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, + link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m"); + + } + r = sd_netlink_message_close_container(m); if (r < 0) return log_link_error_errno(link, r, "Failed to close netlink container: %m"); diff --git a/src/network/networkd-can.h b/src/network/networkd-can.h index c744bdfea..30e99b189 100644 --- a/src/network/networkd-can.h +++ b/src/network/networkd-can.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "conf-parser.h" + typedef struct Link Link; int link_configure_can(Link *link); + +CONFIG_PARSER_PROTOTYPE(config_parse_can_bitrate); diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index 350fea634..233ef9f4f 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -23,11 +23,15 @@ int manager_parse_config_file(Manager *m) { assert(m); - r = config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf", - CONF_PATHS_NULSTR("systemd/networkd.conf.d"), - "Network\0DHCP\0", - config_item_perf_lookup, networkd_gperf_lookup, - CONFIG_PARSE_WARN, m); + r = config_parse_many_nulstr( + PKGSYSCONFDIR "/networkd.conf", + CONF_PATHS_NULSTR("systemd/networkd.conf.d"), + "Network\0" + "DHCP\0", + config_item_perf_lookup, networkd_gperf_lookup, + CONFIG_PARSE_WARN, + m, + NULL); if (r < 0) return r; @@ -140,26 +144,29 @@ int config_parse_duid_rawdata( assert(ret); /* RawData contains DUID in format "NN:NN:NN..." */ - for (;;) { + for (const char *p = rvalue;;) { int n1, n2, len, r; uint32_t byte; _cleanup_free_ char *cbyte = NULL; - r = extract_first_word(&rvalue, &cbyte, ":", 0); + r = extract_first_word(&p, &cbyte, ":", 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); return 0; } if (r == 0) break; + if (count >= MAX_DUID_LEN) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); return 0; } len = strlen(cbyte); if (!IN_SET(len, 1, 2)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); return 0; } n1 = unhexchar(cbyte[0]); @@ -169,7 +176,7 @@ int config_parse_duid_rawdata( n2 = 0; if (n1 < 0 || n2 < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); return 0; } diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 8664d8cdc..ecf9bcea8 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "dhcp-internal.h" +#include "dhcp6-internal.h" #include "escape.h" #include "in-addr-util.h" #include "networkd-dhcp-common.h" @@ -47,7 +48,7 @@ int config_parse_dhcp( else if (streq(rvalue, "both")) s = ADDRESS_FAMILY_YES; else { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue); return 0; } @@ -61,6 +62,50 @@ int config_parse_dhcp( return 0; } +int config_parse_dhcp_route_metric( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = data; + uint32_t metric; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &metric); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue); + return 0; + } + + if (streq_ptr(section, "DHCPv4")) { + network->dhcp_route_metric = metric; + network->dhcp_route_metric_set = true; + } else if (streq_ptr(section, "DHCPv6")) { + network->dhcp6_route_metric = metric; + network->dhcp6_route_metric_set = true; + } else { /* [DHCP] section */ + if (!network->dhcp_route_metric_set) + network->dhcp_route_metric = metric; + if (!network->dhcp6_route_metric_set) + network->dhcp6_route_metric = metric; + } + + return 0; +} + int config_parse_dhcp_use_dns( const char* unit, const char *filename, @@ -83,46 +128,24 @@ int config_parse_dhcp_use_dns( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue); return 0; } - network->dhcp_use_dns = r; - network->dhcp6_use_dns = r; - - return 0; -} - -int config_parse_dhcp_use_sip( - const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse UseSIP=%s, ignoring assignment: %m", rvalue); - return 0; + if (streq_ptr(section, "DHCPv4")) { + network->dhcp_use_dns = r; + network->dhcp_use_dns_set = true; + } else if (streq_ptr(section, "DHCPv6")) { + network->dhcp6_use_dns = r; + network->dhcp6_use_dns_set = true; + } else { /* [DHCP] section */ + if (!network->dhcp_use_dns_set) + network->dhcp_use_dns = r; + if (!network->dhcp6_use_dns_set) + network->dhcp6_use_dns = r; } - network->dhcp_use_sip = r; - return 0; } @@ -148,13 +171,23 @@ int config_parse_dhcp_use_ntp( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue); return 0; } - network->dhcp_use_ntp = r; - network->dhcp6_use_ntp = r; + if (streq_ptr(section, "DHCPv4")) { + network->dhcp_use_ntp = r; + network->dhcp_use_ntp_set = true; + } else if (streq_ptr(section, "DHCPv6")) { + network->dhcp6_use_ntp = r; + network->dhcp6_use_ntp_set = true; + } else { /* [DHCP] section */ + if (!network->dhcp_use_ntp_set) + network->dhcp_use_ntp = r; + if (!network->dhcp6_use_ntp_set) + network->dhcp6_use_ntp = r; + } return 0; } @@ -182,7 +215,7 @@ int config_parse_section_route_table( r = safe_atou32(rvalue, &rt); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue); return 0; } @@ -219,7 +252,7 @@ int config_parse_iaid(const char *unit, r = safe_atou32(rvalue, &iaid); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Unable to read IAID, ignoring assignment: %s", rvalue); return 0; } @@ -230,8 +263,8 @@ int config_parse_iaid(const char *unit, return 0; } -int config_parse_dhcp6_pd_hint( - const char* unit, +int config_parse_dhcp_user_class( + const char *unit, const char *filename, unsigned line, const char *section, @@ -242,27 +275,103 @@ int config_parse_dhcp6_pd_hint( void *data, void *userdata) { - Network *network = data; + char ***l = data; int r; - assert(filename); + assert(l); assert(lvalue); assert(rvalue); - assert(data); - r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue); + if (isempty(rvalue)) { + *l = strv_free(*l); return 0; } - if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length); - network->dhcp6_pd_length = 0; + for (const char *p = rvalue;;) { + _cleanup_free_ char *w = NULL; + + r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to split user classes option, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + return 0; + + if (ltype == AF_INET) { + if (strlen(w) > UINT8_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s length is not in the range 1-255, ignoring.", w); + continue; + } + } else { + if (strlen(w) > UINT16_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s length is not in the range 1-65535, ignoring.", w); + continue; + } + } + + r = strv_push(l, w); + if (r < 0) + return log_oom(); + + w = NULL; + } +} + +int config_parse_dhcp_vendor_class( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + char ***l = data; + int r; + + assert(l); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *l = strv_free(*l); return 0; } - return 0; + for (const char *p = rvalue;;) { + _cleanup_free_ char *w = NULL; + + r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to split vendor classes option, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + return 0; + + if (strlen(w) > UINT8_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s length is not in the range 1-255, ignoring.", w); + continue; + } + + r = strv_push(l, w); + if (r < 0) + return log_oom(); + + w = NULL; + } } int config_parse_dhcp_send_option( @@ -277,14 +386,15 @@ int config_parse_dhcp_send_option( void *data, void *userdata) { - _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt = NULL, *old = NULL; + _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL; + _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL; + uint32_t uint32_data, enterprise_identifier = 0; _cleanup_free_ char *word = NULL, *q = NULL; OrderedHashmap **options = data; + uint16_t u16, uint16_data; union in_addr_union addr; DHCPOptionDataType type; - uint8_t u, uint8_data; - uint16_t uint16_data; - uint32_t uint32_data; + uint8_t u8, uint8_data; const void *udata; const char *p; ssize_t sz; @@ -301,40 +411,73 @@ int config_parse_dhcp_send_option( } p = rvalue; + if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) { + r = extract_first_word(&p, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid DHCP option, ignoring assignment: %s", rvalue); + return 0; + } + + r = safe_atou32(word, &enterprise_identifier); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p); + return 0; + } + word = mfree(word); + } + r = extract_first_word(&p, &word, ":", 0); if (r == -ENOMEM) return log_oom(); - if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + if (r <= 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid DHCP option, ignoring assignment: %s", rvalue); return 0; } - r = safe_atou8(word, &u); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Invalid DHCP option, ignoring assignment: %s", rvalue); - return 0; - } - if (u < 1 || u >= 255) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue); - return 0; + if (ltype == AF_INET6) { + r = safe_atou16(word, &u16); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid DHCP option, ignoring assignment: %s", rvalue); + return 0; + } + if (u16 < 1 || u16 >= UINT16_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue); + return 0; + } + } else { + r = safe_atou8(word, &u8); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid DHCP option, ignoring assignment: %s", rvalue); + return 0; + } + if (u8 < 1 || u8 >= UINT8_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue); + return 0; + } } word = mfree(word); r = extract_first_word(&p, &word, ":", 0); if (r == -ENOMEM) return log_oom(); - if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + if (r <= 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid DHCP option, ignoring assignment: %s", rvalue); return 0; } type = dhcp_option_data_type_from_string(word); if (type < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DHCP option data type, ignoring assignment: %s", p); return 0; } @@ -343,8 +486,8 @@ int config_parse_dhcp_send_option( case DHCP_OPTION_DATA_UINT8:{ r = safe_atou8(p, &uint8_data); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCPv4 uint8 data, ignoring assignment: %s", p); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP uint8 data, ignoring assignment: %s", p); return 0; } @@ -355,8 +498,8 @@ int config_parse_dhcp_send_option( case DHCP_OPTION_DATA_UINT16:{ r = safe_atou16(p, &uint16_data); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCPv4 uint16 data, ignoring assignment: %s", p); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP uint16 data, ignoring assignment: %s", p); return 0; } @@ -367,8 +510,8 @@ int config_parse_dhcp_send_option( case DHCP_OPTION_DATA_UINT32: { r = safe_atou32(p, &uint32_data); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCPv4 uint32 data, ignoring assignment: %s", p); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP uint32 data, ignoring assignment: %s", p); return 0; } @@ -380,8 +523,8 @@ int config_parse_dhcp_send_option( case DHCP_OPTION_DATA_IPV4ADDRESS: { r = in_addr_from_string(AF_INET, p, &addr); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCPv4 ipv4address data, ignoring assignment: %s", p); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p); return 0; } @@ -389,11 +532,23 @@ int config_parse_dhcp_send_option( sz = sizeof(addr.in.s_addr); break; } + case DHCP_OPTION_DATA_IPV6ADDRESS: { + r = in_addr_from_string(AF_INET6, p, &addr); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p); + return 0; + } + + udata = &addr.in6; + sz = sizeof(addr.in6.s6_addr); + break; + } case DHCP_OPTION_DATA_STRING: sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q); if (sz < 0) { - log_syntax(unit, LOG_ERR, filename, line, sz, - "Failed to decode DHCPv4 option data, ignoring assignment: %s", p); + log_syntax(unit, LOG_WARNING, filename, line, sz, + "Failed to decode DHCP option data, ignoring assignment: %s", p); } udata = q; @@ -402,30 +557,119 @@ int config_parse_dhcp_send_option( return -EINVAL; } - r = sd_dhcp_option_new(u, udata, sz, &opt); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store DHCPv4 option '%s', ignoring assignment: %m", rvalue); - return 0; + if (ltype == AF_INET6) { + r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; + } + + r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops); + if (r < 0) + return log_oom(); + + /* Overwrite existing option */ + old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16)); + r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; + } + TAKE_PTR(opt6); + } else { + r = sd_dhcp_option_new(u8, udata, sz, &opt4); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; + } + + r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops); + if (r < 0) + return log_oom(); + + /* Overwrite existing option */ + old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8)); + r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); + return 0; + } + TAKE_PTR(opt4); } - - r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops); - if (r < 0) - return log_oom(); - - /* Overwrite existing option */ - old = ordered_hashmap_remove(*options, UINT_TO_PTR(u)); - r = ordered_hashmap_put(*options, UINT_TO_PTR(u), opt); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store DHCPv4 option '%s', ignoring assignment: %m", rvalue); - return 0; - } - - TAKE_PTR(opt); return 0; } +int config_parse_dhcp_request_options( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = data; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + if (ltype == AF_INET) + network->dhcp_request_options = set_free(network->dhcp_request_options); + else + network->dhcp6_request_options = set_free(network->dhcp6_request_options); + + return 0; + } + + for (p = rvalue;;) { + _cleanup_free_ char *n = NULL; + uint32_t i; + + r = extract_first_word(&p, &n, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP request option, ignoring assignment: %s", + rvalue); + return 0; + } + if (r == 0) + return 0; + + r = safe_atou32(n, &i); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "DHCP request option is invalid, ignoring assignment: %s", n); + continue; + } + + if (i < 1 || i >= UINT8_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n); + continue; + } + + r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options, + NULL, UINT32_TO_PTR(i)); + if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP request option '%s', ignoring assignment: %m", n); + } +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); @@ -443,6 +687,7 @@ static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = { [DHCP_OPTION_DATA_UINT32] = "uint32", [DHCP_OPTION_DATA_STRING] = "string", [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address", + [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address", }; DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType); diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 1d6ddbb8c..01400a238 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -21,6 +21,7 @@ typedef enum DHCPOptionDataType { DHCP_OPTION_DATA_UINT32, DHCP_OPTION_DATA_STRING, DHCP_OPTION_DATA_IPV4ADDRESS, + DHCP_OPTION_DATA_IPV6ADDRESS, _DHCP_OPTION_DATA_MAX, _DHCP_OPTION_DATA_INVALID, } DHCPOptionDataType; @@ -41,11 +42,13 @@ const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_; DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_dhcp); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_metric); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_sip); CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_vendor_class); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_option); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options); diff --git a/src/network/networkd-dhcp-server-bus.c b/src/network/networkd-dhcp-server-bus.c new file mode 100644 index 000000000..1731d0bea --- /dev/null +++ b/src/network/networkd-dhcp-server-bus.c @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "sd-dhcp-server.h" + +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "bus-util.h" +#include "dhcp-server-internal.h" +#include "networkd-dhcp-server-bus.h" +#include "networkd-link-bus.h" +#include "networkd-manager.h" +#include "strv.h" + +static int property_get_leases( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + Link *l = userdata; + sd_dhcp_server *s; + DHCPLease *lease; + Iterator i; + int r; + + assert(reply); + assert(l); + + s = l->dhcp_server; + if (!s) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname); + + r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(lease, s->leases_by_client_id, i) { + r = sd_bus_message_open_container(reply, 'r', "uayayayayt"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "u", (uint32_t)AF_INET); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &lease->address, sizeof(lease->address)); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &lease->gateway, sizeof(lease->gateway)); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &lease->chaddr, sizeof(lease->chaddr)); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(reply, 't', &lease->expiration); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int dhcp_server_emit_changed(Link *link, const char *property, ...) { + _cleanup_free_ char *path = NULL; + char **l; + + assert(link); + + path = link_bus_path(link); + if (!path) + return log_oom(); + + l = strv_from_stdarg_alloca(property); + + return sd_bus_emit_properties_changed_strv( + link->manager->bus, + path, + "org.freedesktop.network1.DHCPServer", + l); +} + +void dhcp_server_callback(sd_dhcp_server *s, uint64_t event, void *data) { + Link *l = data; + + assert(l); + + if (event & SD_DHCP_SERVER_EVENT_LEASE_CHANGED) + (void) dhcp_server_emit_changed(l, "Leases", NULL); +} + + +const sd_bus_vtable dhcp_server_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Leases", "a(uayayayayt)", property_get_leases, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_VTABLE_END +}; diff --git a/src/network/networkd-dhcp-server-bus.h b/src/network/networkd-dhcp-server-bus.h new file mode 100644 index 000000000..49164ff0b --- /dev/null +++ b/src/network/networkd-dhcp-server-bus.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" +#include "networkd-link.h" + +extern const sd_bus_vtable dhcp_server_vtable[]; + +void dhcp_server_callback(sd_dhcp_server *server, uint64_t event, void *data); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index bee75a693..5129a2e37 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -2,14 +2,17 @@ #include "sd-dhcp-server.h" +#include "fd-util.h" +#include "fileio.h" #include "networkd-dhcp-server.h" #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" #include "parse-util.h" -#include "strv.h" +#include "socket-netlink.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" static Address* link_find_dhcp_server_address(Link *link) { Address *address; @@ -18,68 +21,116 @@ static Address* link_find_dhcp_server_address(Link *link) { assert(link->network); /* The first statically configured address if there is any */ - LIST_FOREACH(addresses, address, link->network->static_addresses) { - - if (address->family != AF_INET) - continue; - - if (in_addr_is_null(address->family, &address->in_addr)) - continue; - - return address; - } + LIST_FOREACH(addresses, address, link->network->static_addresses) + if (address->family == AF_INET && + !in_addr_is_null(address->family, &address->in_addr)) + return address; /* If that didn't work, find a suitable address we got from the pool */ - LIST_FOREACH(addresses, address, link->pool_addresses) { - if (address->family != AF_INET) - continue; - - return address; - } + LIST_FOREACH(addresses, address, link->pool_addresses) + if (address->family == AF_INET) + return address; return NULL; } -static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { +static int link_push_uplink_to_dhcp_server( + Link *link, + sd_dhcp_lease_server_type what, + sd_dhcp_server *s) { + _cleanup_free_ struct in_addr *addresses = NULL; size_t n_addresses = 0, n_allocated = 0; - unsigned i; + bool use_dhcp_lease_data = true; - log_link_debug(link, "Copying DNS server information from link"); + assert(link); if (!link->network) return 0; + assert(link->network); - for (i = 0; i < link->network->n_dns; i++) { - struct in_addr ia; + log_link_debug(link, "Copying %s from link", dhcp_lease_server_type_to_string(what)); - /* Only look for IPv4 addresses */ - if (link->network->dns[i].family != AF_INET) - continue; + switch (what) { - ia = link->network->dns[i].address.in; + case SD_DHCP_LEASE_DNS: + /* For DNS we have a special case. We the data configured explicitly locally along with the + * data from the DHCP lease. */ - /* Never propagate obviously borked data */ - if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) - continue; + for (unsigned i = 0; i < link->network->n_dns; i++) { + struct in_addr ia; - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) - return log_oom(); + /* Only look for IPv4 addresses */ + if (link->network->dns[i]->family != AF_INET) + continue; - addresses[n_addresses++] = ia; + ia = link->network->dns[i]->address.in; + + /* Never propagate obviously borked data */ + if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia; + } + + use_dhcp_lease_data = link->network->dhcp_use_dns; + 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. */ + + STRV_FOREACH(i, link->network->ntp) { + union in_addr_union ia; + + if (in_addr_from_string(AF_INET, *i, &ia) < 0) + continue; + + /* Never propagate obviously borked data */ + if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia.in; + } + + use_dhcp_lease_data = link->network->dhcp_use_ntp; + break; } - if (link->network->dhcp_use_dns && link->dhcp_lease) { - const struct in_addr *da = NULL; - int j, n; + case SD_DHCP_LEASE_SIP: - n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); + /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */ + use_dhcp_lease_data = link->network->dhcp_use_sip; + break; + + case SD_DHCP_LEASE_POP3: + case SD_DHCP_LEASE_SMTP: + case SD_DHCP_LEASE_LPR: + /* For the other server types we currently do not allow local configuration of server data, + * since there are typically no local consumers of the data. */ + break; + + default: + assert_not_reached("Unexpected server type"); + } + + if (use_dhcp_lease_data && link->dhcp_lease) { + const struct in_addr *da; + + int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da); if (n > 0) { - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) return log_oom(); - for (j = 0; j < n; j++) + for (int j = 0; j < n; j++) if (in4_addr_is_non_local(&da[j])) addresses[n_addresses++] = da[j]; } @@ -88,105 +139,88 @@ static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (n_addresses <= 0) return 0; - return sd_dhcp_server_set_dns(s, addresses, n_addresses); + return sd_dhcp_server_set_servers(s, what, addresses, n_addresses); } -static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { - _cleanup_free_ struct in_addr *addresses = NULL; - size_t n_addresses = 0, n_allocated = 0; - char **a; +static int dhcp4_server_parse_dns_server_string_and_warn(Link *l, const char *string, struct in_addr **addresses, size_t *n_allocated, size_t *n_addresses) { + for (;;) { + _cleanup_free_ char *word = NULL, *server_name = NULL; + union in_addr_union address; + int family, r, ifindex = 0; - if (!link->network) - return 0; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; - log_link_debug(link, "Copying NTP server information from link"); - - STRV_FOREACH(a, link->network->ntp) { - union in_addr_union ia; + r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name); + if (r < 0) { + log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word); + continue; + } /* Only look for IPv4 addresses */ - if (in_addr_from_string(AF_INET, *a, &ia) <= 0) + if (family != AF_INET) continue; /* Never propagate obviously borked data */ - if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) + if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in)) continue; - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + if (!GREEDY_REALLOC(*addresses, *n_allocated, *n_addresses + 1)) return log_oom(); - addresses[n_addresses++] = ia.in; + (*addresses)[(*n_addresses)++] = address.in; } - if (link->network->dhcp_use_ntp && link->dhcp_lease) { - const struct in_addr *da = NULL; - int j, n; - - n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da); - if (n > 0) { - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) - return log_oom(); - - for (j = 0; j < n; j++) - if (in4_addr_is_non_local(&da[j])) - addresses[n_addresses++] = da[j]; - } - } - - if (n_addresses <= 0) - return 0; - - return sd_dhcp_server_set_ntp(s, addresses, n_addresses); + return 0; } -static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) { +static int dhcp4_server_set_dns_from_resolve_conf(Link *link) { _cleanup_free_ struct in_addr *addresses = NULL; size_t n_addresses = 0, n_allocated = 0; - char **a; + _cleanup_fclose_ FILE *f = NULL; + int n = 0, r; - if (!link->network) - return 0; + f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re"); + if (!f) { + if (errno == ENOENT) + return 0; - log_link_debug(link, "Copying SIP server information from link"); - - STRV_FOREACH(a, link->network->sip) { - union in_addr_union ia; - - /* Only look for IPv4 addresses */ - if (in_addr_from_string(AF_INET, *a, &ia) <= 0) - continue; - - /* Never propagate obviously borked data */ - if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) - continue; - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) - return log_oom(); - - addresses[n_addresses++] = ia.in; + return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m"); } - if (link->network->dhcp_use_sip && link->dhcp_lease) { - const struct in_addr *da = NULL; - int j, n; + for (;;) { + _cleanup_free_ char *line = NULL; + const char *a; + char *l; - n = sd_dhcp_lease_get_sip(link->dhcp_lease, &da); - if (n > 0) { + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m"); + if (r == 0) + break; - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) - return log_oom(); + n++; - for (j = 0; j < n; j++) - if (in4_addr_is_non_local(&da[j])) - addresses[n_addresses++] = da[j]; - } + l = strstrip(line); + if (IN_SET(*l, '#', ';', 0)) + continue; + + a = first_word(l, "nameserver"); + if (!a) + continue; + + r = dhcp4_server_parse_dns_server_string_and_warn(link, a, &addresses, &n_allocated, &n_addresses); + if (r < 0) + log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); } if (n_addresses <= 0) return 0; - return sd_dhcp_server_set_sip(s, addresses, n_addresses); + return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses); } int dhcp4_server_configure(Link *link) { @@ -228,57 +262,41 @@ int dhcp4_server_configure(Link *link) { return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m"); } - if (link->network->dhcp_server_emit_dns) { - if (link->network->n_dhcp_server_dns > 0) - r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns); - else { - uplink = manager_find_uplink(link->manager, link); - acquired_uplink = true; + for (sd_dhcp_lease_server_type type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) { - if (!uplink) { - log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); - r = 0; - } else - r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server); - } - if (r < 0) - log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); - } + if (!link->network->dhcp_server_emit[type].emit) + continue; - if (link->network->dhcp_server_emit_ntp) { - if (link->network->n_dhcp_server_ntp > 0) - r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp); + if (link->network->dhcp_server_emit[type].n_addresses > 0) + /* Explicitly specified servers to emit */ + r = sd_dhcp_server_set_servers( + link->dhcp_server, + type, + link->network->dhcp_server_emit[type].addresses, + link->network->dhcp_server_emit[type].n_addresses); else { - if (!acquired_uplink) + /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */ + if (!acquired_uplink) { uplink = manager_find_uplink(link->manager, link); + acquired_uplink = true; + } - if (!uplink) { - log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); - r = 0; - } else - r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server); - + if (uplink && uplink->network) + r = link_push_uplink_to_dhcp_server(uplink, type, link->dhcp_server); + else if (type == SD_DHCP_LEASE_DNS) + r = dhcp4_server_set_dns_from_resolve_conf(link); + else { + log_link_debug(link, + "Not emitting %s on link, couldn't find suitable uplink.", + dhcp_lease_server_type_to_string(type)); + continue; + } } + if (r < 0) - log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); - } - - if (link->network->dhcp_server_emit_sip) { - if (link->network->n_dhcp_server_sip > 0) - r = sd_dhcp_server_set_sip(link->dhcp_server, link->network->dhcp_server_sip, link->network->n_dhcp_server_sip); - else { - if (!acquired_uplink) - uplink = manager_find_uplink(link->manager, link); - - if (!uplink) { - log_link_debug(link, "Not emitting sip server information on link, couldn't find suitable uplink."); - r = 0; - } else - r = link_push_uplink_sip_to_dhcp_server(uplink, link->dhcp_server); - - } - if (r < 0) - log_link_warning_errno(link, r, "Failed to set SIP server for DHCP server, ignoring: %m"); + log_link_warning_errno(link, r, + "Failed to set %s for DHCP server, ignoring: %m", + dhcp_lease_server_type_to_string(type)); } r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); @@ -312,6 +330,14 @@ int dhcp4_server_configure(Link *link) { return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); } + ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) { + r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); + } + if (!sd_dhcp_server_is_running(link->dhcp_server)) { r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) @@ -321,7 +347,7 @@ int dhcp4_server_configure(Link *link) { return 0; } -int config_parse_dhcp_server_dns( +int config_parse_dhcp_server_emit( const char *unit, const char *filename, unsigned line, @@ -333,78 +359,21 @@ int config_parse_dhcp_server_dns( void *data, void *userdata) { - Network *n = data; - const char *p = rvalue; - int r; + NetworkDHCPServerEmitAddress *emit = data; - assert(filename); - assert(lvalue); + assert(emit); assert(rvalue); - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL; union in_addr_union a; - struct in_addr *m; + int r; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to extract word, ignoring: %s", rvalue); - return 0; - } - if (r == 0) - break; - - r = in_addr_from_string(AF_INET, w, &a); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DNS server address '%s', ignoring assignment: %m", w); - continue; - } - - m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr)); - if (!m) - return log_oom(); - - m[n->n_dhcp_server_dns++] = a.in; - n->dhcp_server_dns = m; - } - - return 0; -} - -int config_parse_dhcp_server_ntp( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *n = data; - const char *p = rvalue; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *w = NULL; - union in_addr_union a; - struct in_addr *m; - - r = extract_first_word(&p, &w, NULL, 0); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); return 0; } @@ -413,68 +382,16 @@ int config_parse_dhcp_server_ntp( r = in_addr_from_string(AF_INET, w, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse NTP server address '%s', ignoring: %m", w); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s= address '%s', ignoring: %m", lvalue, w); continue; } - m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr)); + struct in_addr *m = reallocarray(emit->addresses, emit->n_addresses + 1, sizeof(struct in_addr)); if (!m) return log_oom(); - m[n->n_dhcp_server_ntp++] = a.in; - n->dhcp_server_ntp = m; - } -} - -int config_parse_dhcp_server_sip( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *n = data; - const char *p = rvalue; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *w = NULL; - union in_addr_union a; - struct in_addr *m; - - r = extract_first_word(&p, &w, NULL, 0); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to extract word, ignoring: %s", rvalue); - return 0; - } - if (r == 0) - return 0; - - r = in_addr_from_string(AF_INET, w, &a); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse SIP server address '%s', ignoring: %m", w); - continue; - } - - m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr)); - if (!m) - return log_oom(); - - m[n->n_dhcp_server_sip++] = a.in; - n->dhcp_server_sip = m; + emit->addresses = m; + emit->addresses[emit->n_addresses++] = a.in; } } diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index c90d48ec0..2250a30af 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -9,6 +9,4 @@ typedef struct Link Link; int dhcp4_server_configure(Link *link); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_sip); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 13e3e32f4..722e0d213 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -5,6 +5,7 @@ #include #include +#include "escape.h" #include "alloc-util.h" #include "dhcp-client-internal.h" #include "hostname-util.h" @@ -17,45 +18,76 @@ #include "string-table.h" #include "string-util.h" #include "sysctl-util.h" +#include "web-util.h" -static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all); -static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all); -static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all); -static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback); -static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link); -static int dhcp_lease_renew(sd_dhcp_client *client, Link *link); +static int dhcp4_update_address(Link *link, bool announce); +static int dhcp4_remove_all(Link *link); +static int dhcp4_release_old_lease(Link *link, bool force); -void dhcp4_release_old_lease(Link *link) { - struct in_addr address = {}, address_old = {}; +static int dhcp4_address_callback(Address *address) { + assert(address); + assert(address->link); + + /* Do not call this callback again. */ + address->callback = NULL; + + return dhcp4_release_old_lease(address->link, true); +} + +static int dhcp4_release_old_lease(Link *link, bool force) { + Route *route; + Iterator i; + int k, r = 0; assert(link); - if (!link->dhcp_lease_old) - return; + if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old)) + return 0; - assert(link->dhcp_lease); + if (!force && (link->dhcp_address && !address_is_ready(link->dhcp_address))) { + log_link_debug(link, "New DHCPv4 address is not ready. The old lease will be removed later."); + link->dhcp_address->callback = dhcp4_address_callback; + return 0; + } - (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old); - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); + log_link_debug(link, "Removing old DHCPv4 address and routes."); - (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false); - (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false); - (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false); - - if (!in4_addr_equal(&address_old, &address)) - (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL); - - link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old); link_dirty(link); + + SET_FOREACH(route, link->dhcp_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + if (link->dhcp_address_old) { + k = address_remove(link->dhcp_address_old, link, NULL); + if (k < 0) + r = k; + } + + return r; } static void dhcp4_check_ready(Link *link) { - if (link->dhcp4_messages == 0) { - link->dhcp4_configured = true; - /* New address and routes are configured now. Let's release old lease. */ - dhcp4_release_old_lease(link); - link_check_ready(link); + int r; + + if (link->network->dhcp_send_decline && !link->dhcp4_address_bind) + return; + + if (link->dhcp4_messages > 0) + return; + + link->dhcp4_configured = true; + + /* New address and routes are configured now. Let's release old lease. */ + r = dhcp4_release_old_lease(link, false); + if (r < 0) { + link_enter_failed(link); + return; } + + link_check_ready(link); } static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -84,25 +116,18 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } - if (link->dhcp4_messages == 0) { - if (link->dhcp4_route_failed) { - struct in_addr address = {}; + if (link->dhcp4_messages == 0 && link->dhcp4_route_failed) { + link->dhcp4_route_failed = false; + link->dhcp4_route_retrying = true; - link->dhcp4_route_failed = false; - link->dhcp4_route_retrying = true; - - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); - (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_router(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_address(link, link->dhcp_lease, &address, dhcp_remove_address_handler); - - return 1; - } - if (!link->network->dhcp_send_decline) - dhcp4_check_ready(link); + r = dhcp4_remove_all(link); + if (r < 0) + link_enter_failed(link); + return 1; } + dhcp4_check_ready(link); + return 1; } @@ -125,27 +150,25 @@ static bool link_prefixroute(Link *link) { link->manager->dhcp4_prefix_root_cannot_set_table; } -static int dhcp_route_configure(Route **route, Link *link) { +static int dhcp_route_configure(Route *route, Link *link) { + Route *ret; int r; assert(route); - assert(*route); assert(link); - if (set_contains(link->dhcp_routes, *route)) - return 0; - - r = route_configure(*route, link, dhcp4_route_handler); - if (r <= 0) - return r; + r = route_configure(route, link, dhcp4_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m"); link->dhcp4_messages++; - r = set_put(link->dhcp_routes, *route); + r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret); if (r < 0) - return r; + return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m"); + + (void) set_remove(link->dhcp_routes_old, ret); - TAKE_PTR(*route); return 0; } @@ -175,7 +198,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) { r = route_new(&route); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); + return log_link_error_errno(link, r, "Could not allocate route: %m"); /* Set routes to DNS servers. */ @@ -188,7 +211,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) { route->priority = link->network->dhcp_route_metric; route->table = table; - r = dhcp_route_configure(&route, link); + r = dhcp_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set route to DNS server: %m"); } @@ -232,6 +255,7 @@ static int link_set_dhcp_routes(Link *link) { struct in_addr address; int r, n, i; uint32_t table; + Route *rt; assert(link); @@ -246,12 +270,11 @@ static int link_set_dhcp_routes(Link *link) { * the addresses now, let's not configure the routes either. */ return 0; - r = set_ensure_allocated(&link->dhcp_routes, &route_hash_ops); - if (r < 0) - return log_oom(); - - /* Clear old entries in case the set was already allocated */ - set_clear(link->dhcp_routes); + while ((rt = set_steal_first(link->dhcp_routes))) { + r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m"); + } table = link_get_dhcp_route_table(link); @@ -264,9 +287,9 @@ static int link_set_dhcp_routes(Link *link) { r = dhcp_prefix_route_from_lease(link->dhcp_lease, table, &address, &prefix_route); if (r < 0) - return log_link_error_errno(link, r, "Could not create prefix route: %m"); + return log_link_error_errno(link, r, "Could not create prefix route: %m"); - r = dhcp_route_configure(&prefix_route, link); + r = dhcp_route_configure(prefix_route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set prefix route: %m"); } @@ -317,311 +340,85 @@ static int link_set_dhcp_routes(Link *link) { if (set_contains(link->dhcp_routes, route)) continue; - r = dhcp_route_configure(&route, link); + r = dhcp_route_configure(route, link); if (r < 0) return log_link_error_errno(link, r, "Could not set route: %m"); } } - r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); - if (IN_SET(r, 0, -ENODATA)) - log_link_info(link, "DHCP: No gateway received from DHCP server."); - else if (r < 0) - log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); - else if (in4_addr_is_null(&router[0])) - log_link_info(link, "DHCP: Received gateway is null."); + if (link->network->dhcp_use_gateway) { + r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); + if (IN_SET(r, 0, -ENODATA)) + log_link_info(link, "DHCP: No gateway received from DHCP server."); + else if (r < 0) + log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); + else if (in4_addr_is_null(&router[0])) + log_link_info(link, "DHCP: Received gateway is null."); - /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and - a Router option, the DHCP client MUST ignore the Router option. */ - if (classless_route && static_route) - log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option"); + /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and + a Router option, the DHCP client MUST ignore the Router option. */ + if (classless_route && static_route) + log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option"); - if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) { - _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL; + if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) { + _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL; - r = route_new(&route_gw); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); + r = route_new(&route_gw); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); - /* The dhcp netmask may mask out the gateway. Add an explicit - * route for the gw host so that we can route no matter the - * netmask or existing kernel route tables. */ - route_gw->family = AF_INET; - route_gw->dst.in = router[0]; - route_gw->dst_prefixlen = 32; - route_gw->prefsrc.in = address; - route_gw->scope = RT_SCOPE_LINK; - route_gw->protocol = RTPROT_DHCP; - route_gw->priority = link->network->dhcp_route_metric; - route_gw->table = table; - route_gw->mtu = link->network->dhcp_route_mtu; + /* The dhcp netmask may mask out the gateway. Add an explicit + * route for the gw host so that we can route no matter the + * netmask or existing kernel route tables. */ + route_gw->family = AF_INET; + route_gw->dst.in = router[0]; + route_gw->dst_prefixlen = 32; + route_gw->prefsrc.in = address; + route_gw->scope = RT_SCOPE_LINK; + route_gw->protocol = RTPROT_DHCP; + route_gw->priority = link->network->dhcp_route_metric; + route_gw->table = table; + route_gw->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure(&route_gw, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not set host route: %m"); + r = dhcp_route_configure(route_gw, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set host route: %m"); - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); + r = route_new(&route); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); - route->family = AF_INET; - route->gw.in = router[0]; - route->prefsrc.in = address; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = table; - route->mtu = link->network->dhcp_route_mtu; + route->family = AF_INET; + route->gw.in = router[0]; + route->prefsrc.in = address; + route->protocol = RTPROT_DHCP; + route->priority = link->network->dhcp_route_metric; + route->table = table; + route->mtu = link->network->dhcp_route_mtu; - r = dhcp_route_configure(&route, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not set router: %m"); - } + r = dhcp_route_configure(route, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set router: %m"); + } - Route *rt; - LIST_FOREACH(routes, rt, link->network->static_routes) { - if (!rt->gateway_from_dhcp) - continue; + LIST_FOREACH(routes, rt, link->network->static_routes) { + if (!rt->gateway_from_dhcp) + continue; - if (rt->family != AF_INET) - continue; + if (rt->family != AF_INET) + continue; - rt->gw.in = router[0]; + rt->gw.in = router[0]; - r = route_configure(rt, link, dhcp4_route_handler); - if (r < 0) - return log_link_error_errno(link, r, "Could not set gateway: %m"); - if (r > 0) - link->dhcp4_messages++; + r = dhcp_route_configure(rt, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set gateway: %m"); + } } return link_set_dns_routes(link, &address); } -static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) { - _cleanup_free_ sd_dhcp_route **routes = NULL; - uint32_t table; - int n, i, r; - - assert(link); - assert(address); - - if (!link->network->dhcp_use_routes) - return 0; - - n = sd_dhcp_lease_get_routes(lease, &routes); - if (IN_SET(n, 0, -ENODATA)) - return 0; - else if (n < 0) - return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m"); - - table = link_get_dhcp_route_table(link); - - for (i = 0; i < n; i++) { - _cleanup_(route_freep) Route *route = NULL; - - r = route_new(&route); - if (r < 0) - return log_oom(); - - route->family = AF_INET; - assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0); - assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0); - assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); - route->priority = link->network->dhcp_route_metric; - route->table = table; - route->scope = route_scope_from_address(route, address); - if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE)) - route->prefsrc.in = *address; - - if (!remove_all && set_contains(link->dhcp_routes, route)) - continue; - - (void) route_remove(route, link, NULL); - } - - return n; -} - -static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) { - _cleanup_(route_freep) Route *route_gw = NULL, *route = NULL; - const struct in_addr *router; - uint32_t table; - int r; - - assert(link); - assert(address); - - if (!link->network->dhcp_use_routes) - return 0; - - r = sd_dhcp_lease_get_router(lease, &router); - if (IN_SET(r, 0, -ENODATA)) { - log_link_debug(link, "DHCP: No gateway received from DHCP server."); - return 0; - } else if (r < 0) - return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m"); - else if (in4_addr_is_null(&router[0])) { - log_link_info(link, "DHCP: Received gateway is null, ignoring."); - return 0; - } - - table = link_get_dhcp_route_table(link); - - r = route_new(&route_gw); - if (r < 0) - return log_oom(); - - route_gw->family = AF_INET; - route_gw->dst.in = router[0]; - route_gw->dst_prefixlen = 32; - route_gw->prefsrc.in = *address; - route_gw->scope = RT_SCOPE_LINK; - route_gw->protocol = RTPROT_DHCP; - route_gw->priority = link->network->dhcp_route_metric; - route_gw->table = table; - - if (remove_all || !set_contains(link->dhcp_routes, route_gw)) - (void) route_remove(route_gw, link, NULL); - - r = route_new(&route); - if (r < 0) - return log_oom(); - - route->family = AF_INET; - route->gw.in = router[0]; - route->prefsrc.in = *address; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = table; - - if (remove_all || !set_contains(link->dhcp_routes, route)) - (void) route_remove(route, link, NULL); - - Route *rt; - LIST_FOREACH(routes, rt, link->network->static_routes) { - if (!rt->gateway_from_dhcp) - continue; - - if (rt->family != AF_INET) - continue; - - if (!remove_all && in4_addr_equal(router, &rt->gw.in)) - continue; - - (void) route_remove(rt, link, NULL); - } - - return 0; -} - -static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) { - const struct in_addr *dns; - uint32_t table; - int i, n, r; - - assert(link); - assert(lease); - assert(link->network); - - if (!link->network->dhcp_use_dns || - !link->network->dhcp_routes_to_dns) - return 0; - - n = sd_dhcp_lease_get_dns(lease, &dns); - if (IN_SET(n, 0, -ENODATA)) - return 0; - if (n < 0) - return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m"); - - table = link_get_dhcp_route_table(link); - - for (i = 0; i < n; i ++) { - _cleanup_(route_freep) Route *route = NULL; - - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - route->family = AF_INET; - route->dst.in = dns[i]; - route->dst_prefixlen = 32; - route->prefsrc.in = *address; - route->scope = RT_SCOPE_LINK; - route->protocol = RTPROT_DHCP; - route->priority = link->network->dhcp_route_metric; - route->table = table; - - if (!remove_all && set_contains(link->dhcp_routes, route)) - continue; - - (void) route_remove(route, link, NULL); - } - - if (!link_prefixroute(link)) { - _cleanup_(route_freep) Route *prefix_route = NULL; - - r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route); - if (r < 0) - return log_link_warning_errno(link, r, "Could not delete prefix route: %m"); - - if (remove_all || !set_contains(link->dhcp_routes, prefix_route)) - (void) route_remove(prefix_route, link, NULL); - } - - return 0; -} - -static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - /* This is only used when retrying to assign the address received from DHCPv4 server. - * See dhcp4_route_handler(). */ - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring"); - else - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - (void) dhcp_lease_renew(link->dhcp_client, link); - return 1; -} - -static int dhcp_remove_address( - Link *link, sd_dhcp_lease *lease, - const struct in_addr *address, - link_netlink_message_handler_t callback) { - - _cleanup_(address_freep) Address *a = NULL; - struct in_addr netmask; - int r; - - assert(link); - assert(address); - - if (in4_addr_is_null(address)) - return 0; - - r = address_new(&a); - if (r < 0) - return log_oom(); - - a->family = AF_INET; - a->in_addr.in = *address; - - if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0) - a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - - (void) address_remove(a, link, callback); - - return 0; -} - static int dhcp_reset_mtu(Link *link) { uint16_t mtu; int r; @@ -672,8 +469,86 @@ static int dhcp_reset_hostname(Link *link) { return 0; } +static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->dhcp4_remove_messages > 0); + + link->dhcp4_remove_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 != -ESRCH) + log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 route, ignoring"); + + if (link->dhcp4_remove_messages == 0) { + r = dhcp4_update_address(link, false); + if (r < 0) + link_enter_failed(link); + } + + return 1; +} + +static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->dhcp4_remove_messages > 0); + + link->dhcp4_remove_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 != -EADDRNOTAVAIL) + log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring"); + else + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->dhcp4_remove_messages == 0) { + r = dhcp4_update_address(link, false); + if (r < 0) + link_enter_failed(link); + } + + return 1; +} + +static int dhcp4_remove_all(Link *link) { + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + SET_FOREACH(route, link->dhcp_routes, i) { + k = route_remove(route, link, dhcp4_remove_route_handler); + if (k < 0) + r = k; + else + link->dhcp4_remove_messages++; + } + + if (link->dhcp_address) { + k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler); + if (k < 0) + r = k; + else + link->dhcp4_remove_messages++; + } + + return r; +} + static int dhcp_lease_lost(Link *link) { - struct in_addr address = {}; + int k, r = 0; assert(link); assert(link->dhcp_lease); @@ -682,18 +557,27 @@ static int dhcp_lease_lost(Link *link) { link->dhcp4_configured = false; - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address); - (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_router(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true); - (void) dhcp_remove_address(link, link->dhcp_lease, &address, NULL); - (void) dhcp_reset_mtu(link); - (void) dhcp_reset_hostname(link); + /* dhcp_lease_lost() may be called during renewing IP address. */ + k = dhcp4_release_old_lease(link, true); + if (k < 0) + r = k; + + k = dhcp4_remove_all(link); + if (k < 0) + r = k; + + k = dhcp_reset_mtu(link); + if (k < 0) + r = k; + + k = dhcp_reset_hostname(link); + if (k < 0) + r = k; link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link_dirty(link); - return 0; + return r; } static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { @@ -718,6 +602,7 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { (void) in_addr_to_string(AF_INET, &address, &pretty); log_link_debug(link, "Successfully claimed DHCP4 address %s", strna(pretty)); } + link->dhcp4_address_bind = true; dhcp4_check_ready(link); break; @@ -726,7 +611,9 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) { (void) in_addr_to_string(AF_INET, &address, &pretty); log_link_warning(link, "DAD conflict. Dropping DHCP4 address %s", strna(pretty)); - (void) sd_dhcp_client_send_decline(link->dhcp_client); + r = sd_dhcp_client_send_decline(link->dhcp_client); + if (r < 0) + log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m"); if (link->dhcp_lease) { r = dhcp_lease_lost(link); @@ -768,6 +655,44 @@ static int configure_dhcpv4_duplicate_address_detection(Link *link) { return 0; } +static int dhcp4_start_acd(Link *link) { + union in_addr_union addr; + int r; + + if (!link->network->dhcp_send_decline) + return 0; + + if (!link->dhcp_lease) + return 0; + + link->dhcp4_address_bind = false; + + r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in); + if (r < 0) + return r; + + r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in); + if (r < 0) + return r; + + r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link); + if (r < 0) + return r; + + if (DEBUG_LOGGING) { + _cleanup_free_ char *pretty = NULL; + + (void) in_addr_to_string(AF_INET, &addr, &pretty); + log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty)); + } + + r = sd_ipv4acd_start(link->network->dhcp_acd, true); + if (r < 0) + return r; + + return 1; +} + static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -797,95 +722,46 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } - if (link->network->dhcp_send_decline) { - union in_addr_union addr; + r = dhcp4_start_acd(link); + if (r < 0) { + log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCP4 adddress: %m"); + link_enter_failed(link); + return 1; + } - (void) sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in); - - r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in); - if (r < 0) - return r; - - r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link); - if (r < 0) - return r; - - if (DEBUG_LOGGING) { - _cleanup_free_ char *pretty = NULL; - - (void) in_addr_to_string(AF_INET, &addr, &pretty); - log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty)); - } - - r = sd_ipv4acd_start(link->network->dhcp_acd, true); - if (r < 0) - log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m"); - } else - dhcp4_check_ready(link); + dhcp4_check_ready(link); return 1; } -static int dhcp4_update_address(Link *link, - struct in_addr *address, - struct in_addr *netmask, - uint32_t lifetime) { +static int dhcp4_update_address(Link *link, bool announce) { _cleanup_(address_freep) Address *addr = NULL; - unsigned prefixlen; - int r; - - assert(address); - assert(netmask); - assert(lifetime); - - prefixlen = in4_addr_netmask_to_prefixlen(netmask); - - r = address_new(&addr); - if (r < 0) - return r; - - addr->family = AF_INET; - addr->in_addr.in.s_addr = address->s_addr; - addr->cinfo.ifa_prefered = lifetime; - addr->cinfo.ifa_valid = lifetime; - addr->prefixlen = prefixlen; - addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr; - addr->prefix_route = link_prefixroute(link); - - /* allow reusing an existing address and simply update its lifetime - * in case it already exists */ - r = address_configure(addr, link, dhcp4_address_handler, true); - if (r < 0) - return r; - - return 0; -} - -static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { - sd_dhcp_lease *lease; - struct in_addr address; - struct in_addr netmask; uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; + struct in_addr address, netmask; + unsigned prefixlen; + Address *ret; int r; assert(link); - assert(client); assert(link->network); - r = sd_dhcp_client_get_lease(client, &lease); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: no lease: %m"); + if (!link->dhcp_lease) + return 0; - sd_dhcp_lease_unref(link->dhcp_lease); + link_set_state(link, LINK_STATE_CONFIGURING); link->dhcp4_configured = false; - link->dhcp_lease = sd_dhcp_lease_ref(lease); - link_dirty(link); - r = sd_dhcp_lease_get_address(lease, &address); + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they + * are called, the related flags must be cleared. Otherwise, the link becomes configured state + * before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + + r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) return log_link_warning_errno(link, r, "DHCP error: no address: %m"); - r = sd_dhcp_lease_get_netmask(lease, &netmask); + r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); if (r < 0) return log_link_warning_errno(link, r, "DHCP error: no netmask: %m"); @@ -895,70 +771,90 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); } - r = dhcp4_update_address(link, &address, &netmask, lifetime); + prefixlen = in4_addr_netmask_to_prefixlen(&netmask); + + if (announce) { + const struct in_addr *router; + + r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); + if (r < 0 && r != -ENODATA) + return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); + + if (r > 0 && !in4_addr_is_null(&router[0])) + log_struct(LOG_INFO, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", + ADDRESS_FMT_VAL(address), + prefixlen, + ADDRESS_FMT_VAL(router[0])), + "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), + "PREFIXLEN=%u", prefixlen, + "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0])); + else + log_struct(LOG_INFO, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u", + ADDRESS_FMT_VAL(address), + prefixlen), + "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), + "PREFIXLEN=%u", prefixlen); + } + + r = address_new(&addr); if (r < 0) - return log_link_warning_errno(link, r, "Could not update IP address: %m"); + return log_oom(); + + addr->family = AF_INET; + addr->in_addr.in.s_addr = address.s_addr; + addr->cinfo.ifa_prefered = lifetime; + addr->cinfo.ifa_valid = lifetime; + addr->prefixlen = prefixlen; + addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr; + addr->prefix_route = link_prefixroute(link); + + /* allow reusing an existing address and simply update its lifetime + * in case it already exists */ + r = address_configure(addr, link, dhcp4_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m"); + + if (!address_equal(link->dhcp_address, ret)) + link->dhcp_address_old = link->dhcp_address; + link->dhcp_address = ret; return 0; } -static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { - const struct in_addr *router; +static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { + sd_dhcp_lease *lease; + int r; + + assert(link); + assert(client); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no lease: %m"); + + sd_dhcp_lease_unref(link->dhcp_lease); + link->dhcp_lease = sd_dhcp_lease_ref(lease); + link_dirty(link); + + return dhcp4_update_address(link, false); +} + +static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { sd_dhcp_lease *lease; - struct in_addr address; - struct in_addr netmask; - unsigned prefixlen; - uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; int r; assert(client); assert(link); - link->dhcp4_configured = false; - r = sd_dhcp_client_get_lease(client, &lease); if (r < 0) return log_link_error_errno(link, r, "DHCP error: No lease: %m"); - r = sd_dhcp_lease_get_address(lease, &address); - if (r < 0) - return log_link_error_errno(link, r, "DHCP error: No address: %m"); - - r = sd_dhcp_lease_get_netmask(lease, &netmask); - if (r < 0) - return log_link_error_errno(link, r, "DHCP error: No netmask: %m"); - - prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - - if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) { - r = sd_dhcp_lease_get_lifetime(lease, &lifetime); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); - } - - r = sd_dhcp_lease_get_router(lease, &router); - if (r < 0 && r != -ENODATA) - return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); - - if (r > 0 && !in4_addr_is_null(&router[0])) - log_struct(LOG_INFO, - LOG_LINK_INTERFACE(link), - LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", - ADDRESS_FMT_VAL(address), - prefixlen, - ADDRESS_FMT_VAL(router[0])), - "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), - "PREFIXLEN=%u", prefixlen, - "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0])); - else - log_struct(LOG_INFO, - LOG_LINK_INTERFACE(link), - LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u", - ADDRESS_FMT_VAL(address), - prefixlen), - "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), - "PREFIXLEN=%u", prefixlen); - + sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp_lease = sd_dhcp_lease_ref(lease); link_dirty(link); @@ -1009,9 +905,14 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { } } - r = dhcp4_update_address(link, &address, &netmask, lifetime); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update IP address: %m"); + if (link->dhcp4_remove_messages == 0) { + r = dhcp4_update_address(link, true); + if (r < 0) + return r; + } else + log_link_debug(link, + "The link has previously assigned DHCPv4 address or routes. " + "The newly assigned address and routes will set up after old ones are removed."); return 0; } @@ -1019,35 +920,14 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) { int r; - link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease); - - /* On ip address change, to keep the connectability, we would like to assign new address and - * routes, and then release old lease. There are two possible success paths: - * - * 1. new address and routes are configured. - * -> handled by dhcp_release_old_lease() in dhcp4_route_handler(). - * 2. new address is configured and no route is requested. - * -> handled by dhcp_release_old_lease() in dhcp4_address_handler(). - * - * On error in assigning new address and routes, then the link always enters to the failed - * state. And link_enter_failed() leads to the DHCP client to be stopped. So, - * dhcp_release_old_lease() will be also called by link_stop_clients(). - */ - r = dhcp_lease_acquired(client, link); - if (r < 0) { - /* If it fails, then the new address is not configured yet. - * So, let's simply drop the old lease. */ - sd_dhcp_lease_unref(link->dhcp_lease); - link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old); + if (r < 0) (void) dhcp_lease_lost(link); - return r; - } - return 0; + return r; } -static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) { +static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) { sd_dhcp_lease *lease; struct in_addr addr; int r; @@ -1062,12 +942,40 @@ static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) { r = sd_dhcp_lease_get_server_identifier(lease, &addr); if (r < 0) - return log_link_debug_errno(link, r, "Failed to get DHCP server ip address: %m"); + return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m"); - if (set_contains(link->network->dhcp_black_listed_ip, UINT32_TO_PTR(addr.s_addr))) { + if (set_contains(link->network->dhcp_deny_listed_ip, UINT32_TO_PTR(addr.s_addr))) { log_struct(LOG_DEBUG, LOG_LINK_INTERFACE(link), - LOG_LINK_MESSAGE(link, "DHCPv4 ip '%u.%u.%u.%u' found in black listed ip addresses, ignoring offer", + LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in deny-listed IP addresses, ignoring offer", + ADDRESS_FMT_VAL(addr))); + return true; + } + + return false; +} + +static int dhcp_server_is_allow_listed(Link *link, sd_dhcp_client *client) { + sd_dhcp_lease *lease; + struct in_addr addr; + int r; + + assert(link); + assert(link->network); + assert(client); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DHCP lease: %m"); + + r = sd_dhcp_lease_get_server_identifier(lease, &addr); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m"); + + if (set_contains(link->network->dhcp_allow_listed_ip, UINT32_TO_PTR(addr.s_addr))) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in allow-listed IP addresses, accepting offer", ADDRESS_FMT_VAL(addr))); return true; } @@ -1105,8 +1013,11 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { } if (link->dhcp_lease) { - if (link->network->dhcp_send_release) - (void) sd_dhcp_client_send_release(client); + if (link->network->dhcp_send_release) { + r = sd_dhcp_client_send_release(client); + if (r < 0) + log_link_warning_errno(link, r, "Failed to send DHCP RELEASE, ignoring: %m"); + } r = dhcp_lease_lost(link); if (r < 0) { @@ -1159,12 +1070,19 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { } break; case SD_DHCP_CLIENT_EVENT_SELECTING: - r = dhcp_server_is_black_listed(link, client); - if (r < 0) - return r; - if (r != 0) - return -ENOMSG; - + if (!set_isempty(link->network->dhcp_allow_listed_ip)) { + r = dhcp_server_is_allow_listed(link, client); + if (r < 0) + return r; + if (r == 0) + return -ENOMSG; + } else { + r = dhcp_server_is_deny_listed(link, client); + if (r < 0) + return r; + if (r != 0) + return -ENOMSG; + } break; default: if (event < 0) @@ -1334,11 +1252,11 @@ int dhcp4_configure(Link *link) { return log_oom(); if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m"); - } - r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m"); + r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m"); + } r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, @@ -1421,13 +1339,22 @@ int dhcp4_configure(Link *link) { log_link_debug(link, "DHCP4 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option); continue; } - if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option); } ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options, i) { - r = sd_dhcp_client_set_dhcp_option(link->dhcp_client, send_option); + r = sd_dhcp_client_add_option(link->dhcp_client, send_option); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m"); + } + + ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options, i) { + r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option); + if (r == -EEXIST) + continue; if (r < 0) return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m"); } @@ -1443,6 +1370,13 @@ int dhcp4_configure(Link *link) { return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m"); } + if (link->network->dhcp_mudurl) { + r = sd_dhcp_client_set_mud_url(link->dhcp_client, + link->network->dhcp_mudurl); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m"); + } + if (link->network->dhcp_user_class) { r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class); if (r < 0) @@ -1464,7 +1398,13 @@ int dhcp4_configure(Link *link) { if (link->network->ip_service_type > 0) { r = sd_dhcp_client_set_service_type(link->dhcp_client, link->network->ip_service_type); if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m"); + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set IP service type: %m"); + } + + if (link->network->dhcp_fallback_lease_lifetime > 0) { + r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m"); } if (link->network->dhcp_send_decline) { @@ -1508,13 +1448,13 @@ int config_parse_dhcp_max_attempts( r = safe_atou64(rvalue, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue); return 0; } if (a == 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue); return 0; } @@ -1524,7 +1464,7 @@ int config_parse_dhcp_max_attempts( return 0; } -int config_parse_dhcp_black_listed_ip_address( +int config_parse_dhcp_acl_ip_address( const char *unit, const char *filename, unsigned line, @@ -1537,7 +1477,7 @@ int config_parse_dhcp_black_listed_ip_address( void *userdata) { Network *network = data; - const char *p; + Set **acl; int r; assert(filename); @@ -1545,20 +1485,24 @@ int config_parse_dhcp_black_listed_ip_address( assert(rvalue); assert(data); + acl = STR_IN_SET(lvalue, "DenyList", "BlackList") ? &network->dhcp_deny_listed_ip : &network->dhcp_allow_listed_ip; + if (isempty(rvalue)) { - network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip); + *acl = set_free(*acl); return 0; } - for (p = rvalue;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *n = NULL; union in_addr_union ip; r = extract_first_word(&p, &n, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCP black listed ip address, ignoring assignment: %s", - rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCP '%s=' IP address, ignoring assignment: %s", + lvalue, rvalue); return 0; } if (r == 0) @@ -1566,142 +1510,16 @@ int config_parse_dhcp_black_listed_ip_address( r = in_addr_from_string(AF_INET, n, &ip); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "DHCP black listed ip address is invalid, ignoring assignment: %s", n); + log_syntax(unit, LOG_WARNING, filename, line, r, + "DHCP '%s=' IP address is invalid, ignoring assignment: %s", lvalue, n); continue; } - r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL); + r = set_ensure_put(acl, NULL, UINT32_TO_PTR(ip.in.s_addr)); if (r < 0) - return log_oom(); - - r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr)); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store DHCP '%s=' IP address '%s', ignoring assignment: %m", lvalue, n); } - - return 0; -} - -int config_parse_dhcp_user_class( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char ***l = data; - int r; - - assert(l); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - *l = strv_free(*l); - return 0; - } - - for (;;) { - _cleanup_free_ char *w = NULL; - - r = extract_first_word(&rvalue, &w, NULL, 0); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to split user classes option, ignoring: %s", rvalue); - break; - } - if (r == 0) - break; - - if (strlen(w) > 255) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s length is not in the range 1-255, ignoring.", w); - continue; - } - - r = strv_push(l, w); - if (r < 0) - return log_oom(); - - w = NULL; - } - - return 0; -} - -int config_parse_dhcp_request_options( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = data; - const char *p; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - network->dhcp_request_options = set_free(network->dhcp_request_options); - return 0; - } - - for (p = rvalue;;) { - _cleanup_free_ char *n = NULL; - uint32_t i; - - r = extract_first_word(&p, &n, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse DHCP request option, ignoring assignment: %s", - rvalue); - return 0; - } - if (r == 0) - return 0; - - r = safe_atou32(n, &i); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "DHCP request option is invalid, ignoring assignment: %s", n); - continue; - } - - if (i < 1 || i >= 255) { - log_syntax(unit, LOG_ERR, filename, line, r, - "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n); - continue; - } - - r = set_ensure_allocated(&network->dhcp_request_options, NULL); - if (r < 0) - return log_oom(); - - r = set_put(network->dhcp_request_options, UINT32_TO_PTR(i)); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store DHCP request option '%s', ignoring assignment: %m", n); - } - - return 0; } int config_parse_dhcp_ip_service_type( @@ -1731,6 +1549,86 @@ int config_parse_dhcp_ip_service_type( return 0; } +int config_parse_dhcp_mud_url( + 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 *unescaped = NULL; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + network->dhcp_mudurl = mfree(network->dhcp_mudurl); + return 0; + } + + r = cunescape(rvalue, 0, &unescaped); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue); + return 0; + } + + if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse MUD URL '%s', ignoring: %m", rvalue); + + return 0; + } + + return free_and_strdup_warn(&network->dhcp_mudurl, unescaped); +} + +int config_parse_dhcp_fallback_lease_lifetime(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + uint32_t k; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + network->dhcp_fallback_lease_lifetime = 0; + return 0; + } + + /* We accept only "forever" or "infinity". */ + if (STR_IN_SET(rvalue, "forever", "infinity")) + k = CACHE_INFO_INFINITY_LIFE_TIME; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid LeaseLifetime= value, ignoring: %s", rvalue); + return 0; + } + + network->dhcp_fallback_lease_lifetime = k; + + return 0; +} + static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { [DHCP_CLIENT_ID_MAC] = "mac", [DHCP_CLIENT_ID_DUID] = "duid", diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h index 95fa5ee4b..7a80897ff 100644 --- a/src/network/networkd-dhcp4.h +++ b/src/network/networkd-dhcp4.h @@ -17,14 +17,13 @@ typedef enum DHCPClientIdentifier { _DHCP_CLIENT_ID_INVALID = -1, } DHCPClientIdentifier; -void dhcp4_release_old_lease(Link *link); int dhcp4_configure(Link *link); int dhcp4_set_client_identifier(Link *link); int dhcp4_set_promote_secondaries(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 7304270c6..d671284b0 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -6,10 +6,10 @@ #include #include #include -#include "sd-radv.h" #include "sd-dhcp6-client.h" +#include "escape.h" #include "hashmap.h" #include "hostname-util.h" #include "missing_network.h" @@ -17,376 +17,914 @@ #include "networkd-dhcp6.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-radv.h" #include "siphash24.h" +#include "string-table.h" #include "string-util.h" #include "radv-internal.h" +#include "web-util.h" -static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); -static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr); -static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link); -static int dhcp6_prefix_remove_all(Manager *m, Link *link); +static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { + uint32_t lifetime_preferred, lifetime_valid; + union in_addr_union pd_prefix; + uint8_t pd_prefix_len; -static bool dhcp6_get_prefix_delegation(Link *link) { - if (!link->network) + if (!lease) return false; - return IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH); + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + + return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0; } -static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { - Manager *manager; - Link *l; - Iterator i; +DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) { + if (!p) + return NULL; - assert(dhcp6_link); - - manager = dhcp6_link->manager; - assert(manager); - - HASHMAP_FOREACH(l, manager->links, i) { - if (l == dhcp6_link) - continue; - - if (!dhcp6_get_prefix_delegation(l)) - continue; - - return true; + if (p->link && p->link->manager) { + hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix); + set_remove(p->link->manager->dhcp6_pd_prefixes, p); } - return false; + link_unref(p->link); + return mfree(p); } -static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, - Link *link) { +static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) { + assert(p); + + siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state); + siphash24_compress(&p->link, sizeof(p->link), state); +} + +static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) { + int r; + + r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix)); + if (r != 0) + return r; + + return CMP(a->link, b->link); +} + +DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func); + +static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) { + DHCP6DelegatedPrefix *pd; + + assert(link); + assert(link->manager); + assert(prefix); + + pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6); + if (!pd) + return NULL; + + return pd->link; +} + +static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) { + DHCP6DelegatedPrefix *pd, in; + + assert(link); + assert(link->manager); + assert(pd_prefix); + assert(ret_prefix); + + in = (DHCP6DelegatedPrefix) { + .pd_prefix = pd_prefix->in6, + .link = link, + }; + + pd = set_get(link->manager->dhcp6_pd_prefixes, &in); + if (!pd) + return -ENOENT; + + ret_prefix->in6 = pd->prefix; return 0; } -static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, - uint8_t prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { - sd_radv *radv = link->radv; - int r; - _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; +static int dhcp6_pd_remove_old(Link *link, bool force); - r = sd_radv_prefix_new(&p); - if (r < 0) - return r; +static int dhcp6_pd_address_callback(Address *address) { + Address *a; + Iterator i; - r = sd_radv_prefix_set_prefix(p, prefix, prefix_len); - if (r < 0) - return r; + assert(address); + assert(address->link); - r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); - if (r < 0) - return r; + /* Make this called only once */ + SET_FOREACH(a, address->link->dhcp6_pd_addresses, i) + a->callback = NULL; - r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); - if (r < 0) - return r; - - r = sd_radv_stop(radv); - if (r < 0) - return r; - - r = sd_radv_add_prefix(radv, p, true); - if (r < 0 && r != -EEXIST) - return r; - - r = dhcp6_prefix_add(link->manager, prefix, link); - if (r < 0) - return r; - - return sd_radv_start(radv); + return dhcp6_pd_remove_old(address->link, true); } -static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { +static int dhcp6_pd_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + assert(link->manager); + + if (!force && (link->dhcp6_pd_address_messages != 0 || link->dhcp6_pd_route_configured != 0)) + return 0; + + if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old)) + return 0; + + if (!force) { + bool set_callback = !set_isempty(link->dhcp6_pd_addresses); + + SET_FOREACH(address, link->dhcp6_pd_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->dhcp6_pd_addresses, i) + address->callback = dhcp6_pd_address_callback; + return 0; + } + } + + log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_pd_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + + (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); + dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); + } + + SET_FOREACH(address, link->dhcp6_pd_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +int dhcp6_pd_remove(Link *link) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + assert(link->manager); + + link->dhcp6_pd_address_configured = false; + link->dhcp6_pd_route_configured = false; + + k = dhcp6_pd_remove_old(link, true); + if (k < 0) + r = k; + + if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes)) + return r; + + log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_pd_routes, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + + (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); + dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6)); + } + + SET_FOREACH(address, link->dhcp6_pd_addresses, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { int r; assert(link); + assert(link->dhcp6_pd_route_messages > 0); + + link->dhcp6_pd_route_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet"); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route"); + link_enter_failed(link); + return 1; + } + + if (link->dhcp6_pd_route_messages == 0) { + log_link_debug(link, "DHCPv6 prefix delegation routes set"); + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_route_configured = true; + + r = dhcp6_pd_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + link_check_ready(link); + } return 1; } -int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { +static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) { + _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL; + _cleanup_(route_freep) Route *route = NULL; + Link *assigned_link; + Route *ret; int r; - sd_dhcp6_lease *lease; - union in_addr_union pd_prefix; - uint8_t pd_prefix_len; - uint32_t lifetime_preferred, lifetime_valid; - r = sd_dhcp6_client_get_lease(client, &lease); + assert(link); + assert(link->manager); + assert(prefix); + assert(pd_prefix); + + r = route_new(&route); if (r < 0) return r; - sd_dhcp6_lease_reset_pd_prefix_iter(lease); + route->family = AF_INET6; + route->dst = *prefix; + route->dst_prefixlen = 64; - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, - &lifetime_preferred, - &lifetime_valid) >= 0) { - _cleanup_free_ char *buf = NULL; - _cleanup_(route_freep) Route *route = NULL; + r = route_configure(route, link, dhcp6_pd_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m"); - if (pd_prefix_len >= 64) - continue; + link->dhcp6_pd_route_messages++; - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m"); - r = route_new(&route); - if (r < 0) - return r; + (void) set_remove(link->dhcp6_pd_routes_old, ret); - route->family = AF_INET6; - route->dst = pd_prefix; - route->dst_prefixlen = pd_prefix_len; - route->type = RTN_UNREACHABLE; + assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix); + if (assigned_link) { + assert(assigned_link == link); + return 0; + } - r = route_remove(route, link, dhcp6_route_remove_handler); + pd = new(DHCP6DelegatedPrefix, 1); + if (!pd) + return log_oom(); + + *pd = (DHCP6DelegatedPrefix) { + .prefix = prefix->in6, + .pd_prefix = pd_prefix->in6, + .link = link_ref(link), + }; + + r = hashmap_ensure_allocated(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops); + if (r < 0) + return log_oom(); + + r = hashmap_put(link->manager->dhcp6_prefixes, &pd->prefix, pd); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); + + r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m"); + + TAKE_PTR(pd); + return 0; +} + +static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->dhcp6_pd_address_messages > 0); + + link->dhcp6_pd_address_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address"); + link_enter_failed(link); + return 1; + } else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->dhcp6_pd_address_messages == 0) { + log_link_debug(link, "DHCPv6 delegated prefix addresses set"); + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_address_configured = true; + + r = dhcp6_pd_remove_old(link, false); if (r < 0) { - log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; + link_enter_failed(link); + return 1; } - log_link_debug(link, "Removing unreachable route %s/%u", - strnull(buf), pd_prefix_len); + r = link_request_set_routes(link); + if (r < 0) { + link_enter_failed(link); + return 1; + } + } + + return 1; +} + +static int dhcp6_set_pd_address(Link *link, + const union in_addr_union *prefix, + uint8_t prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + + _cleanup_(address_freep) Address *address = NULL; + Address *ret; + int r; + + assert(link); + assert(link->network); + assert(prefix); + + if (!link->network->dhcp6_pd_assign) + return 0; + + r = address_new(&address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m"); + + address->in_addr = *prefix; + + if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_pd_token)) + memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8); + else { + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m"); + } + + address->prefixlen = prefix_len; + address->family = AF_INET6; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, dhcp6_pd_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m"); + + link->dhcp6_pd_address_messages++; + + r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m"); + + (void) set_remove(link->dhcp6_pd_addresses_old, ret); + + return 0; +} + +static int dhcp6_pd_assign_prefix(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix, + uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid) { + int r; + + assert(link); + assert(prefix); + + r = radv_add_prefix(link, &prefix->in6, prefix_len, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + + r = dhcp6_set_pd_route(link, prefix, pd_prefix); + if (r < 0) + return r; + + r = dhcp6_set_pd_address(link, prefix, prefix_len, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + + return 0; +} + +bool link_dhcp6_pd_is_enabled(Link *link) { + if (!link->network) + return false; + + return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6; +} + +static bool link_has_preferred_subnet_id(Link *link) { + if (!link->network) + return false; + + return link->network->dhcp6_pd_subnet_id >= 0; +} + +static int dhcp6_get_preferred_delegated_prefix( + Link *link, + const union in_addr_union *masked_pd_prefix, + uint8_t pd_prefix_len, + union in_addr_union *ret) { + + /* We start off with the original PD prefix we have been assigned and iterate from there */ + union in_addr_union prefix; + uint64_t n_prefixes; + Link *assigned_link; + int r; + + assert(link); + assert(link->manager); + assert(masked_pd_prefix); + assert(pd_prefix_len <= 64); + + n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + prefix = *masked_pd_prefix; + + if (link_has_preferred_subnet_id(link)) { + uint64_t subnet_id = link->network->dhcp6_pd_subnet_id; + + /* If the link has a preference for a particular subnet id try to allocate that */ + if (subnet_id >= n_prefixes) + return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), + "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", + subnet_id, n_prefixes); + + r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id); + if (r < 0) + return log_link_warning_errno(link, r, + "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", + subnet_id, n_prefixes); + + /* Verify that the prefix we did calculate fits in the pd prefix. + * This should not fail as we checked the prefix size beforehand */ + assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0); + + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + if (assigned_link && assigned_link != link) { + _cleanup_free_ char *assigned_buf = NULL; + + (void) in_addr_to_string(AF_INET6, &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; + } + + for (uint64_t n = 0; n < n_prefixes; n++) { + /* If we do not have an allocation preference just iterate + * through the address space and return the first free prefix. */ + assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix); + if (!assigned_link || assigned_link == link) { + *ret = prefix; + return 0; + } + + r = in_addr_prefix_next(AF_INET6, &prefix, 64); + if (r < 0) + return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m"); + } + + return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space."); +} + +static void dhcp6_pd_prefix_distribute(Link *dhcp6_link, + const union in_addr_union *masked_pd_prefix, + uint8_t pd_prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid, + bool assign_preferred_subnet_id) { + + Iterator i; + Link *link; + int r; + + assert(dhcp6_link); + assert(dhcp6_link->manager); + assert(masked_pd_prefix); + assert(pd_prefix_len <= 64); + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + _cleanup_free_ char *assigned_buf = NULL; + union in_addr_union assigned_prefix; + + if (link == dhcp6_link) + continue; + + if (!link_dhcp6_pd_is_enabled(link)) + continue; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + continue; + + if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link)) + continue; + + r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix); + if (r < 0) { + r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix); + if (r < 0) { + link->dhcp6_pd_prefixes_assigned = false; + continue; + } + } + + (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf); + r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix, 64, + lifetime_preferred, lifetime_valid); + if (r < 0) { + log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m", + strna(assigned_buf)); + link_enter_failed(link); + } else + log_link_debug(link, "Assigned prefix %s/64", strna(assigned_buf)); + } +} + +static int dhcp6_pd_prepare(Link *link) { + Address *address; + Route *route; + int r; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + + link_dirty(link); + + link->dhcp6_pd_address_configured = false; + link->dhcp6_pd_route_configured = false; + link->dhcp6_pd_prefixes_assigned = true; + + while ((address = set_steal_first(link->dhcp6_pd_addresses))) { + r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m"); + } + + while ((route = set_steal_first(link->dhcp6_pd_routes))) { + r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m"); } return 0; } -static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, - struct in6_addr *pd_prefix, - uint8_t pd_prefix_len, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { - Link *link; - Manager *manager = dhcp6_link->manager; - union in_addr_union prefix; - uint64_t n_prefixes, n_used = 0; - _cleanup_free_ char *buf = NULL; - _cleanup_free_ char *assigned_buf = NULL; +static int dhcp6_pd_finalize(Link *link) { int r; - assert(manager); - assert(pd_prefix_len <= 64); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; - prefix.in6 = *pd_prefix; + if (!link_dhcp6_pd_is_enabled(link)) + return 0; - r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); + if (link->dhcp6_pd_address_messages == 0) { + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_address_configured = true; + } else { + log_link_debug(link, "Setting DHCPv6 PD addresses"); + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->dhcp6_pd_route_messages == 0) { + if (link->dhcp6_pd_prefixes_assigned) + link->dhcp6_pd_route_configured = true; + } else + log_link_debug(link, "Setting DHCPv6 PD routes"); + + r = dhcp6_pd_remove_old(link, false); if (r < 0) return r; - n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); - (void) in_addr_to_string(AF_INET6, &prefix, &buf); - log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", - n_prefixes, strnull(buf), pd_prefix_len); + return 0; +} - while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) { - Link *assigned_link; +static void dhcp6_pd_prefix_lost(Link *dhcp6_link) { + Link *link; + Iterator i; + int r; - if (n_used == n_prefixes) { - log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u", - n_used, n_prefixes, strnull(buf), pd_prefix_len); - - return -EAGAIN; - } + assert(dhcp6_link); + assert(dhcp6_link->manager); + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { if (link == dhcp6_link) continue; - if (!dhcp6_get_prefix_delegation(link)) + if (!link_dhcp6_pd_is_enabled(link)) continue; - assigned_link = dhcp6_prefix_get(manager, &prefix.in6); - if (assigned_link && assigned_link != link) - continue; + r = dhcp6_pd_remove(link); + if (r < 0) + link_enter_failed(link); + } +} - (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); - r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64, - lifetime_preferred, lifetime_valid); - if (r < 0) { - log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m", - assigned_link ? "update": "assign", - strnull(assigned_buf), - strnull(buf), pd_prefix_len); +static int dhcp6_remove_old(Link *link, bool force); - if (!assigned_link) - continue; +static int dhcp6_address_callback(Address *address) { + Address *a; + Iterator i; - } else - log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link", - n_used + 1, n_prefixes, - strnull(assigned_buf), - strnull(buf), pd_prefix_len); + assert(address); + assert(address->link); - n_used++; + /* Make this called only once */ + SET_FOREACH(a, address->link->dhcp6_addresses, i) + a->callback = NULL; - r = in_addr_prefix_next(AF_INET6, &prefix, 64); - if (r < 0 && n_used < n_prefixes) - return r; + return dhcp6_remove_old(address->link, true); +} + +static int dhcp6_remove_old(Link *link, bool force) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + if (!force && (!link->dhcp6_address_configured || !link->dhcp6_route_configured)) + return 0; + + if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old)) + return 0; + + if (!force) { + bool set_callback = !set_isempty(link->dhcp6_addresses); + + SET_FOREACH(address, link->dhcp6_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->dhcp6_addresses, i) + address->callback = dhcp6_address_callback; + return 0; + } } - return 0; + log_link_debug(link, "Removing old DHCPv6 addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(address, link->dhcp6_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; +} + +static int dhcp6_remove(Link *link) { + Address *address; + Route *route; + Iterator i; + int k, r = 0; + + assert(link); + + link->dhcp6_address_configured = false; + link->dhcp6_route_configured = false; + + k = dhcp6_remove_old(link, true); + if (k < 0) + r = k; + + if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes)) + return r; + + log_link_debug(link, "Removing DHCPv6 addresses and routes."); + + link_dirty(link); + + SET_FOREACH(route, link->dhcp6_routes, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(address, link->dhcp6_addresses, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + return r; } static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { int r; assert(link); + assert(link->dhcp6_route_messages > 0); + + link->dhcp6_route_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_message_warning_errno(link, m, r, "Received error when adding unreachable route for DHCPv6 delegated subnet"); + if (r < 0 && r != -EEXIST) { + log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet"); + link_enter_failed(link); + return 1; + } + + if (link->dhcp6_route_messages == 0) { + log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set"); + link->dhcp6_route_configured = true; + + r = dhcp6_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + link_check_ready(link); + } return 1; } -static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { +static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) { + _cleanup_(route_freep) Route *route = NULL; + _cleanup_free_ char *buf = NULL; + Route *ret; int r; - sd_dhcp6_lease *lease; - union in_addr_union pd_prefix; - uint8_t pd_prefix_len; - uint32_t lifetime_preferred, lifetime_valid; - Iterator i = ITERATOR_FIRST; - r = sd_dhcp6_client_get_lease(client, &lease); - if (r < 0) - return r; + assert(link); + assert(addr); - sd_dhcp6_lease_reset_pd_prefix_iter(lease); + (void) in_addr_to_string(AF_INET6, addr, &buf); - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, - &lifetime_preferred, - &lifetime_valid) >= 0) { - - _cleanup_free_ char *buf = NULL; - - (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); - - if (pd_prefix_len > 64) { - log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", - strnull(buf), pd_prefix_len); - continue; - } - - if (pd_prefix_len < 48) - log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", - strnull(buf), pd_prefix_len); - - if (pd_prefix_len < 64) { - _cleanup_(route_freep) Route *route = NULL; - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; - route->dst = pd_prefix; - route->dst_prefixlen = pd_prefix_len; - route->table = link_get_dhcp_route_table(link); - route->type = RTN_UNREACHABLE; - - r = route_configure(route, link, dhcp6_route_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m", - strnull(buf), - pd_prefix_len); - continue; - } - - log_link_debug(link, "Configuring unreachable route for %s/%u", - strnull(buf), pd_prefix_len); - } else - log_link_debug(link, "Not adding a blocking route since distributed prefix is /64"); - - r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6, - pd_prefix_len, - lifetime_preferred, - lifetime_valid); - if (r < 0 && r != -EAGAIN) - return r; - - if (r >= 0) - i = ITERATOR_FIRST; + if (prefixlen > 64) { + log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", + strna(buf), prefixlen); + return 0; } - return 0; + if (prefixlen == 64) { + log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s/64 since distributed prefix is 64", + strna(buf)); + return 1; + } + + if (prefixlen < 48) + log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", + strna(buf), prefixlen); + + r = route_new(&route); + if (r < 0) + return log_oom(); + + route->family = AF_INET6; + route->dst = *addr; + route->dst_prefixlen = prefixlen; + route->table = link_get_dhcp_route_table(link); + route->type = RTN_UNREACHABLE; + + r = route_configure(route, link, dhcp6_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strna(buf), prefixlen); + + link->dhcp6_route_messages++; + + r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strna(buf), prefixlen); + + (void) set_remove(link->dhcp6_routes_old, ret); + + return 1; } -int dhcp6_request_prefix_delegation(Link *link) { - Link *l; +static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { Iterator i; + Link *link; + int r; - assert_return(link, -EINVAL); - assert_return(link->manager, -EOPNOTSUPP); + assert(dhcp6_link); + assert(dhcp6_link->dhcp6_lease); - if (dhcp6_get_prefix_delegation(link) <= 0) - return 0; - - log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); - - HASHMAP_FOREACH(l, link->manager->links, i) { - int r, enabled; - - if (l == link) + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) continue; - if (!l->dhcp6_client) + r = dhcp6_pd_prepare(link); + if (r < 0) + link_enter_failed(link); + } + + for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) { + uint32_t lifetime_preferred, lifetime_valid; + union in_addr_union pd_prefix, prefix; + uint8_t pd_prefix_len; + + r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len, + &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; + + r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len); + if (r < 0) + return r; + if (r == 0) continue; - r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link"); - continue; + /* We are doing prefix allocation in two steps: + * 1. all those links that have a preferred subnet id will be assigned their subnet + * 2. all those links that remain will receive prefixes in sequential order. Prefixes + * that were previously already allocated to another link will be skipped. + * The assignment has to be split in two phases since subnet id + * preferences should be honored. Meaning that any subnet id should be + * handed out to the requesting link and not to some link that didn't + * specify any preference. */ + + assert(pd_prefix_len <= 64); + + prefix = pd_prefix; + r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); + if (r < 0) + return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m"); + + if (DEBUG_LOGGING) { + uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); + _cleanup_free_ char *buf = NULL; + + (void) in_addr_to_string(AF_INET6, &prefix, &buf); + log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", + n_prefixes, strna(buf), pd_prefix_len); } - if (enabled == 0) { - r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link"); - continue; - } - } + dhcp6_pd_prefix_distribute(dhcp6_link, + &prefix, + pd_prefix_len, + lifetime_preferred, + lifetime_valid, + true); - r = sd_dhcp6_client_is_running(l->dhcp6_client); - if (r <= 0) + dhcp6_pd_prefix_distribute(dhcp6_link, + &prefix, + pd_prefix_len, + lifetime_preferred, + lifetime_valid, + false); + } + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) continue; - if (enabled != 0) { - log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); - (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l); - - continue; - } - - r = sd_dhcp6_client_stop(l->dhcp6_client); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link"); - continue; - } - - r = sd_dhcp6_client_start(l->dhcp6_client); - if (r < 0) { - log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link"); - continue; - } - - log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); + r = dhcp6_pd_finalize(link); + if (r < 0) + link_enter_failed(link); } return 0; @@ -396,6 +934,9 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * int r; assert(link); + assert(link->dhcp6_address_messages > 0); + + link->dhcp6_address_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -408,28 +949,40 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * } else if (r >= 0) (void) manager_rtnl_process_address(rtnl, m, link->manager); - r = link_request_set_routes(link); - if (r < 0) { - link_enter_failed(link); - return 1; + if (link->dhcp6_address_messages == 0) { + log_link_debug(link, "DHCPv6 addresses set"); + link->dhcp6_address_configured = true; + + r = dhcp6_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + r = link_request_set_routes(link); + if (r < 0) { + link_enter_failed(link); + return 1; + } } return 1; } -static int dhcp6_address_change( +static int dhcp6_update_address( Link *link, - struct in6_addr *ip6_addr, + const struct in6_addr *ip6_addr, uint32_t lifetime_preferred, uint32_t lifetime_valid) { _cleanup_(address_freep) Address *addr = NULL; _cleanup_free_ char *buffer = NULL; + Address *ret; int r; r = address_new(&addr); if (r < 0) - return r; + return log_oom(); addr->family = AF_INET6; addr->in_addr.in6 = *ip6_addr; @@ -439,34 +992,41 @@ static int dhcp6_address_change( addr->cinfo.ifa_valid = lifetime_valid; (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer); - log_link_info(link, - "DHCPv6 address %s/%d timeout preferred %d valid %d", - strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); + log_link_info(link, "DHCPv6 address %s/%u timeout preferred %d valid %d", + strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid); - r = address_configure(addr, link, dhcp6_address_handler, true); + r = address_configure(addr, link, dhcp6_address_handler, true, &ret); if (r < 0) - return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); + return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s/%u: %m", + strna(buffer), addr->prefixlen); + + link->dhcp6_address_messages++; + + r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s/%u: %m", + strna(buffer), addr->prefixlen); + + (void) set_remove(link->dhcp6_addresses_old, ret); return 0; } -static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { +static int dhcp6_address_acquired(Link *link) { int r; - sd_dhcp6_lease *lease; - struct in6_addr ip6_addr; - uint32_t lifetime_preferred, lifetime_valid; - r = sd_dhcp6_client_get_lease(client, &lease); - if (r < 0) - return r; + assert(link); + assert(link->dhcp6_lease); - sd_dhcp6_lease_reset_address_iter(lease); + for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) { + uint32_t lifetime_preferred, lifetime_valid; + struct in6_addr ip6_addr; - while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { + r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid); + if (r < 0) + break; - r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); + r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) return r; } @@ -474,9 +1034,104 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { return 0; } -static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { +static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) { + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL; + sd_dhcp6_lease *lease; + Address *a; + Route *rt; int r; + + link->dhcp6_address_configured = false; + link->dhcp6_route_configured = false; + + link_dirty(link); + + while ((a = set_steal_first(link->dhcp6_addresses))) { + r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m"); + } + + while ((rt = set_steal_first(link->dhcp6_routes))) { + r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m"); + } + + r = sd_dhcp6_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m"); + + lease_old = TAKE_PTR(link->dhcp6_lease); + link->dhcp6_lease = sd_dhcp6_lease_ref(lease); + + r = dhcp6_address_acquired(link); + if (r < 0) + return r; + + if (dhcp6_lease_has_pd_prefix(lease)) { + r = dhcp6_pd_prefix_acquired(link); + if (r < 0) + return r; + } else if (dhcp6_lease_has_pd_prefix(lease_old)) + /* When we had PD prefixes but not now, we need to remove them. */ + dhcp6_pd_prefix_lost(link); + + if (link->dhcp6_address_messages == 0) + link->dhcp6_address_configured = true; + else { + log_link_debug(link, "Setting DHCPv6 addresses"); + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->dhcp6_route_messages == 0) + link->dhcp6_route_configured = true; + else + log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets"); + + r = dhcp6_remove_old(link, false); + if (r < 0) + return r; + + if (link->dhcp6_address_configured && link->dhcp6_route_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; +} + +static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) { + return 0; +} + +static int dhcp6_lease_lost(Link *link) { + int r; + + assert(link); + assert(link->manager); + + log_link_info(link, "DHCPv6 lease lost"); + + if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) + dhcp6_pd_prefix_lost(link); + + link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); + + r = dhcp6_remove(link); + if (r < 0) + return r; + + return 0; +} + +static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { Link *link = userdata; + int r; assert(link); assert(link->network); @@ -484,41 +1139,27 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { + switch (event) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - if (sd_dhcp6_client_get_lease(client, NULL) >= 0) - log_link_warning(link, "DHCPv6 lease lost"); - - (void) dhcp6_lease_pd_prefix_lost(client, link); - (void) dhcp6_prefix_remove_all(link->manager, link); - - link_dirty(link); - link->dhcp6_configured = false; + r = dhcp6_lease_lost(link); + if (r < 0) + link_enter_failed(link); break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: - r = dhcp6_lease_address_acquired(client, link); + r = dhcp6_lease_ip_acquired(client, link); if (r < 0) { link_enter_failed(link); return; } - r = dhcp6_lease_pd_prefix_acquired(client, link); - if (r < 0) - log_link_debug(link, "DHCPv6 did not receive prefixes to delegate"); - _fallthrough_; case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: r = dhcp6_lease_information_acquired(client, link); - if (r < 0) { + if (r < 0) link_enter_failed(link); - return; - } - - link_dirty(link); - link->dhcp6_configured = true; break; default: @@ -528,8 +1169,6 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { log_link_warning(link, "DHCPv6 unknown event: %d", event); return; } - - link_check_ready(link); } int dhcp6_request_address(Link *link, int ir) { @@ -539,7 +1178,7 @@ int dhcp6_request_address(Link *link, int ir) { assert(link); assert(link->dhcp6_client); assert(link->network); - assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0); r = sd_dhcp6_client_is_running(link->dhcp6_client); if (r < 0) @@ -553,9 +1192,8 @@ int dhcp6_request_address(Link *link, int ir) { if (pd && ir && link->network->dhcp6_force_pd_other_information) { log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set"); - r = sd_dhcp6_client_set_address_request(link->dhcp6_client, - false); - if (r < 0 ) + r = sd_dhcp6_client_set_address_request(link->dhcp6_client, false); + if (r < 0) return r; ir = false; @@ -589,6 +1227,81 @@ int dhcp6_request_address(Link *link, int ir) { return 0; } +int dhcp6_request_prefix_delegation(Link *link) { + Link *l; + Iterator i; + + assert(link); + assert(link->manager); + + if (!link_dhcp6_pd_is_enabled(link)) + return 0; + + log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); + + HASHMAP_FOREACH(l, link->manager->links, i) { + int r, enabled; + + if (l == link) + continue; + + if (!l->dhcp6_client) + continue; + + r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link: %m"); + link_enter_failed(l); + continue; + } + + if (enabled == 0) { + r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link: %m"); + link_enter_failed(l); + continue; + } + } + + r = sd_dhcp6_client_is_running(l->dhcp6_client); + if (r <= 0) + continue; + + if (enabled != 0) { + if (dhcp6_lease_has_pd_prefix(l->dhcp6_lease)) { + log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); + r = dhcp6_pd_prefix_acquired(l); + if (r < 0) + link_enter_failed(l); + } + continue; + } + + r = sd_dhcp6_client_stop(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link: %m"); + link_enter_failed(l); + continue; + } + + r = sd_dhcp6_client_start(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link: %m"); + link_enter_failed(l); + continue; + } + + log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); + } + + /* dhcp6_pd_prefix_acquired() may make the link in failed state. */ + if (link->state == LINK_STATE_FAILED) + return -ENOANO; + + return 0; +} + static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { _cleanup_free_ char *hostname = NULL; const char *hn; @@ -618,9 +1331,33 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { return 0; } +static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { + Link *link; + Iterator i; + + assert(dhcp6_link); + assert(dhcp6_link->manager); + + HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) { + if (link == dhcp6_link) + continue; + + if (!link_dhcp6_pd_is_enabled(link)) + continue; + + return true; + } + + return false; +} + int dhcp6_configure(Link *link) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + sd_dhcp6_option *vendor_option; + sd_dhcp6_option *send_option; + void *request_options; const DUID *duid; + Iterator i; int r; assert(link); @@ -662,6 +1399,14 @@ int dhcp6_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m"); + ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options, i) { + r = sd_dhcp6_client_add_option(client, send_option); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m"); + } + r = dhcp6_set_hostname(client, link); if (r < 0) return r; @@ -676,6 +1421,44 @@ int dhcp6_configure(Link *link) { return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m"); } + if (link->network->dhcp6_mudurl) { + r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m"); + } + + SET_FOREACH(request_options, link->network->dhcp6_request_options, i) { + uint32_t option = PTR_TO_UINT32(request_options); + + r = sd_dhcp6_client_set_request_option(client, option); + if (r == -EEXIST) { + log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option); + continue; + } + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option); + } + + if (link->network->dhcp6_user_class) { + r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m"); + } + + if (link->network->dhcp6_vendor_class) { + r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m"); + } + + ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options, i) { + r = sd_dhcp6_client_add_vendor_option(client, vendor_option); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m"); + } + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m"); @@ -697,142 +1480,177 @@ int dhcp6_configure(Link *link) { return 0; } -static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) { - assert_return(m, NULL); - assert_return(addr, NULL); +int config_parse_dhcp6_pd_hint( + 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) { - return hashmap_get(m->dhcp6_prefixes, addr); -} - -static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { + Network *network = data; int r; - assert(link); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_warning_errno(link, m, r, "Received error adding DHCPv6 Prefix Delegation route"); - link_enter_failed(link); - return 1; - } - - return 1; -} - -static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { - _cleanup_(route_freep) Route *route = NULL; - _cleanup_free_ struct in6_addr *a = NULL; - _cleanup_free_ char *buf = NULL; - Link *assigned_link; - int r; - - assert_return(m, -EINVAL); - assert_return(addr, -EINVAL); - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; - route->dst.in6 = *addr; - route->dst_prefixlen = 64; - - r = route_configure(route, link, dhcp6_route_add_handler); - if (r < 0) - return r; - - (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf); - log_link_debug(link, "Adding prefix route %s/64", strnull(buf)); - - assigned_link = hashmap_get(m->dhcp6_prefixes, addr); - if (assigned_link) { - assert(assigned_link == link); + r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue); return 0; } - a = newdup(struct in6_addr, addr, 1); - if (!a) - return -ENOMEM; - - r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(m->dhcp6_prefixes, a, link); - if (r < 0) - return r; - - TAKE_PTR(a); - link_ref(link); - return 0; -} - -static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) { - log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal"); - link_enter_failed(link); - return 1; + if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length); + network->dhcp6_pd_length = 0; + return 0; } - return 1; + return 0; } -int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) { - _cleanup_free_ struct in6_addr *a = NULL; - _cleanup_(link_unrefp) Link *l = NULL; - _cleanup_(route_freep) Route *route = NULL; - _cleanup_free_ char *buf = NULL; +int config_parse_dhcp6_mud_url( + 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 *unescaped = NULL; + Network *network = data; int r; - assert_return(m, -EINVAL); - assert_return(addr, -EINVAL); + assert(filename); + assert(lvalue); + assert(rvalue); - l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a); - if (!l) - return -EINVAL; + if (isempty(rvalue)) { + network->dhcp6_mudurl = mfree(network->dhcp6_mudurl); + return 0; + } - (void) sd_radv_remove_prefix(l->radv, addr, 64); + r = cunescape(rvalue, 0, &unescaped); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue); + return 0; + } - r = route_new(&route); - if (r < 0) - return r; + if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse MUD URL '%s', ignoring: %m", rvalue); + return 0; + } - route->family = AF_INET6; - route->dst.in6 = *addr; - route->dst_prefixlen = 64; + return free_and_replace(network->dhcp6_mudurl, unescaped); +} - r = route_remove(route, l, dhcp6_prefix_remove_handler); - if (r < 0) - return r; +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode, + "Failed to parse WithoutRA= setting"); - (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf); - log_link_debug(l, "Removing prefix route %s/64", strnull(buf)); +static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = { + [DHCP6_CLIENT_START_MODE_NO] = "no", + [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request", + [DHCP6_CLIENT_START_MODE_SOLICIT] = "solicit", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode); + +int config_parse_dhcp6_pd_subnet_id( + 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) { + + int64_t *p = data; + uint64_t t; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue) || streq(rvalue, "auto")) { + *p = -1; + return 0; + } + + r = safe_atoux64(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (t > INT64_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid subnet id '%s', ignoring assignment.", + rvalue); + return 0; + } + + *p = (int64_t) t; return 0; } -static int dhcp6_prefix_remove_all(Manager *m, Link *link) { - struct in6_addr *addr; - Iterator i; - Link *l; +int config_parse_dhcp6_pd_token( + 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) { - assert_return(m, -EINVAL); - assert_return(link, -EINVAL); + union in_addr_union *addr = data, tmp; + int r; - HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) - if (l == link) - (void) dhcp6_prefix_remove(m, addr); + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *addr = IN_ADDR_NULL; + return 0; + } + + r = in_addr_from_string(AF_INET6, rvalue, &tmp); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue); + return 0; + } + + if (in_addr_is_null(AF_INET6, &tmp)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue); + return 0; + } + + *addr = tmp; return 0; } diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 26d810f40..214456096 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -4,12 +4,39 @@ #include "sd-dhcp6-client.h" #include "conf-parser.h" +#include "macro.h" + +typedef enum DHCP6ClientStartMode { + DHCP6_CLIENT_START_MODE_NO, + DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST, + DHCP6_CLIENT_START_MODE_SOLICIT, + _DHCP6_CLIENT_START_MODE_MAX, + _DHCP6_CLIENT_START_MODE_INVALID = -1, +} DHCP6ClientStartMode; typedef struct Link Link; typedef struct Manager Manager; -int dhcp6_request_prefix_delegation(Link *link); +typedef struct DHCP6DelegatedPrefix { + struct in6_addr prefix; /* Prefix assigned to the link */ + struct in6_addr pd_prefix; /* PD prefix provided by DHCP6 lease */ + Link *link; +} DHCP6DelegatedPrefix; + +DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free); + +bool link_dhcp6_pd_is_enabled(Link *link); +int dhcp6_pd_remove(Link *link); int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link, int ir); -int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link); -int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr); +int dhcp6_request_prefix_delegation(Link *link); + +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_token); + +const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_; +DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index df1868224..833d13cf0 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -218,7 +218,7 @@ int config_parse_fdb_hwaddr( r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue); return 0; } @@ -293,7 +293,7 @@ int config_parse_fdb_destination( r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr); if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, + return log_syntax(unit, LOG_WARNING, filename, line, r, "FDB destination IP address is invalid, ignoring assignment: %s", rvalue); @@ -331,14 +331,14 @@ int config_parse_fdb_vxlan_vni( r = safe_atou32(rvalue, &vni); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s", rvalue); return 0; } if (vni > VXLAN_VID_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s", rvalue); return 0; @@ -379,7 +379,7 @@ int config_parse_fdb_ntf_flags( f = fdb_ntf_flags_from_string(rvalue); if (f < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL), "FDB failed to parse AssociatedWith=, ignoring assignment: %s", rvalue); return 0; diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index d74d37559..aaabb3d1b 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -20,5 +20,6 @@ struct ConfigPerfItem; %% Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter) Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec) +Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes) DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 5750ea091..e844799b5 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -17,7 +17,7 @@ static int ipv4ll_address_lost(Link *link) { assert(link); - link->ipv4ll_address = false; + link->ipv4ll_address_configured = false; r = sd_ipv4ll_get_address(link->ipv4ll, &addr); if (r < 0) @@ -47,7 +47,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link int r; assert(link); - assert(!link->ipv4ll_address); + assert(!link->ipv4ll_address_configured); r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -57,7 +57,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link } else if (r >= 0) (void) manager_rtnl_process_address(rtnl, m, link->manager); - link->ipv4ll_address = true; + link->ipv4ll_address_configured = true; link_check_ready(link); return 1; @@ -71,7 +71,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { assert(ll); assert(link); - link->ipv4ll_address = false; + link->ipv4ll_address_configured = false; r = sd_ipv4ll_get_address(ll, &address); if (r == -ENOENT) @@ -92,7 +92,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); ll_addr->scope = RT_SCOPE_LINK; - r = address_configure(ll_addr, link, ipv4ll_address_handler, false); + r = address_configure(ll_addr, link, ipv4ll_address_handler, false, NULL); if (r < 0) return r; @@ -154,6 +154,10 @@ int ipv4ll_configure(Link *link) { r = sd_ipv4ll_new(&link->ipv4ll); if (r < 0) return r; + + r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); + if (r < 0) + return r; } if (link->sd_device && @@ -163,10 +167,6 @@ int ipv4ll_configure(Link *link) { return r; } - r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); - if (r < 0) - return r; - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); if (r < 0) return r; @@ -208,7 +208,7 @@ int config_parse_ipv4ll( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=%s, ignoring assignment. " "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.", lvalue, rvalue, lvalue); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index b207866c7..7051ba9da 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -113,17 +113,18 @@ int config_parse_ipv6_proxy_ndp_address( r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address); if (r < 0) - return r; + return log_oom(); r = in_addr_from_string(AF_INET6, rvalue, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s", + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse IPv6 proxy NDP address, ignoring: %s", rvalue); return 0; } if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue); return 0; } diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index efb72b60e..f623a9b4a 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -6,14 +6,16 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" +#include "bus-message-util.h" #include "bus-polkit.h" -#include "bus-util.h" #include "dns-domain.h" #include "networkd-link-bus.h" #include "networkd-link.h" #include "networkd-manager.h" #include "parse-util.h" #include "resolve-util.h" +#include "socket-netlink.h" #include "strv.h" #include "user-util.h" @@ -108,15 +110,18 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_ strv_free_and_replace(l->ntp, ntp); - (void) link_dirty(l); + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); } -int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ struct in_addr_data *dns = NULL; - size_t allocated = 0, n = 0; +static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) { + struct in_addr_full **dns; Link *l = userdata; + size_t n; int r; assert(message); @@ -126,52 +131,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ if (r < 0) return r; - r = sd_bus_message_enter_container(message, 'a', "(iay)"); - if (r < 0) - return r; - - for (;;) { - int family; - size_t sz; - const void *d; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_enter_container(message, 'r', "iay"); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_read(message, "i", &family); - if (r < 0) - return r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - - r = sd_bus_message_read_array(message, 'y', &d, &sz); - if (r < 0) - return r; - if (sz != FAMILY_ADDRESS_SIZE(family)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); - - if (!dns_server_address_valid(family, d)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address"); - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(dns, allocated, n+1)) - return -ENOMEM; - - dns[n].family = family; - memcpy(&dns[n].address, d, sz); - n++; - } - - r = sd_bus_message_exit_container(message); + r = bus_message_read_dns_servers(message, error, extended, &dns, &n); if (r < 0) return r; @@ -180,16 +140,40 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ NULL, true, UID_INVALID, &l->manager->polkit_registry, error); if (r < 0) - return r; - if (r == 0) - return 1; /* Polkit will call us back */ + goto finalize; + if (r == 0) { + r = 1; /* Polkit will call us back */ + goto finalize; + } + + if (l->n_dns != (unsigned) -1) + for (unsigned i = 0; i < l->n_dns; i++) + in_addr_full_free(l->dns[i]); free_and_replace(l->dns, dns); l->n_dns = n; - (void) link_dirty(l); + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); + +finalize: + for (size_t i = 0; i < n; i++) + in_addr_full_free(dns[i]); + free(dns); + + return r; +} + +int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_link_method_set_dns_servers_internal(message, userdata, error, false); +} + +int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_link_method_set_dns_servers_internal(message, userdata, error, true); } int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -262,7 +246,10 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_ l->search_domains = TAKE_PTR(search_domains); l->route_domains = TAKE_PTR(route_domains); - (void) link_dirty(l); + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); } @@ -293,7 +280,11 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s if (l->dns_default_route != b) { l->dns_default_route = b; - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; } return sd_bus_reply_method_return(message, NULL); @@ -335,7 +326,11 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er if (l->llmnr != mode) { l->llmnr = mode; - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; } return sd_bus_reply_method_return(message, NULL); @@ -377,7 +372,11 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err if (l->mdns != mode) { l->mdns = mode; - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; } return sd_bus_reply_method_return(message, NULL); @@ -419,7 +418,11 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd if (l->dns_over_tls_mode != mode) { l->dns_over_tls_mode = mode; - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; } return sd_bus_reply_method_return(message, NULL); @@ -461,7 +464,11 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e if (l->dnssec_mode != mode) { l->dnssec_mode = mode; - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; } return sd_bus_reply_method_return(message, NULL); @@ -498,7 +505,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v return -ENOMEM; STRV_FOREACH(i, ntas) { - r = set_put_strdup(ns, *i); + r = set_put_strdup(&ns, *i); if (r < 0) return r; } @@ -515,7 +522,10 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v set_free_free(l->dnssec_negative_trust_anchors); l->dnssec_negative_trust_anchors = TAKE_PTR(ns); - (void) link_dirty(l); + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); } @@ -541,7 +551,11 @@ int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_e return 1; /* Polkit will call us back */ link_ntp_settings_clear(l); - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; return sd_bus_reply_method_return(message, NULL); } @@ -567,7 +581,40 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e return 1; /* Polkit will call us back */ link_dns_settings_clear(l); - (void) link_dirty(l); + + link_dirty(l); + r = link_save_and_clean(l); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + int r; + + assert(l); + + if (!l->network) + return sd_bus_error_setf(error, BUS_ERROR_UNMANAGED_INTERFACE, + "Interface %s is not managed by systemd-networkd", + l->ifname); + + r = bus_verify_polkit_async(message, CAP_NET_ADMIN, + "org.freedesktop.network1.forcerenew", + NULL, true, UID_INVALID, + &l->manager->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Polkit will call us back */ + + if (l->dhcp_server) { + r = sd_dhcp_server_forcerenew(l->dhcp_server); + if (r < 0) + return r; + } return sd_bus_reply_method_return(message, NULL); } @@ -622,10 +669,9 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_ return r; link_set_state(l, LINK_STATE_INITIALIZED); - r = link_save(l); + r = link_save_and_clean(l); if (r < 0) return r; - link_clean(l); return sd_bus_reply_method_return(message, NULL); } @@ -641,6 +687,7 @@ const sd_bus_vtable link_vtable[] = { SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetDNSEx", "a(iayqs)", NULL, bus_link_method_set_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), @@ -651,6 +698,7 @@ const sd_bus_vtable link_vtable[] = { SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ForceRenew", NULL, NULL, bus_link_method_force_renew, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END @@ -730,6 +778,9 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void if (r < 0) return 0; + if (streq(interface, "org.freedesktop.network1.DHCPServer") && !link->dhcp_server) + return 0; + *found = link; return 1; diff --git a/src/network/networkd-link-bus.h b/src/network/networkd-link-bus.h index 09e4ad68a..94474f22f 100644 --- a/src/network/networkd-link-bus.h +++ b/src/network/networkd-link-bus.h @@ -21,6 +21,7 @@ int property_get_address_state(sd_bus *bus, const char *path, const char *interf int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error); @@ -31,4 +32,5 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index bc7bf2595..0057d184f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "alloc-util.h" @@ -31,10 +32,10 @@ #include "networkd-manager.h" #include "networkd-ndisc.h" #include "networkd-neighbor.h" +#include "networkd-sriov.h" #include "networkd-radv.h" #include "networkd-routing-policy-rule.h" #include "networkd-wifi.h" -#include "qdisc.h" #include "set.h" #include "socket-util.h" #include "stat-util.h" @@ -42,6 +43,7 @@ #include "string-table.h" #include "strv.h" #include "sysctl-util.h" +#include "tc.h" #include "tmpfile-util.h" #include "udev-util.h" #include "util.h" @@ -346,6 +348,9 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) { if (!netdev) return; + if (netdev->ifindex <= 0) + return; + if (link_get(link->manager, netdev->ifindex, &master) < 0) return; @@ -661,6 +666,9 @@ void link_ntp_settings_clear(Link *link) { } void link_dns_settings_clear(Link *link) { + if (link->n_dns != (unsigned) -1) + for (unsigned i = 0; i < link->n_dns; i++) + in_addr_full_free(link->dns[i]); link->dns = mfree(link->dns); link->n_dns = (unsigned) -1; @@ -683,7 +691,6 @@ static void link_free_engines(Link *link) { link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server); link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); - link->dhcp_routes = set_free(link->dhcp_routes); link->lldp = sd_lldp_unref(link->lldp); @@ -691,6 +698,7 @@ static void link_free_engines(Link *link) { link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll); link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease); link->ndisc = sd_ndisc_unref(link->ndisc); link->radv = sd_radv_unref(link->radv); } @@ -703,17 +711,32 @@ static Link *link_free(Link *link) { link_ntp_settings_clear(link); link_dns_settings_clear(link); - link->routes = set_free_with_destructor(link->routes, route_free); - link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free); + link->routes = set_free(link->routes); + link->routes_foreign = set_free(link->routes_foreign); + link->dhcp_routes = set_free(link->dhcp_routes); + link->dhcp_routes_old = set_free(link->dhcp_routes_old); + link->dhcp6_routes = set_free(link->dhcp6_routes); + link->dhcp6_routes_old = set_free(link->dhcp6_routes_old); + link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes); + link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old); + link->ndisc_routes = set_free(link->ndisc_routes); + link->ndisc_routes_old = set_free(link->ndisc_routes_old); - link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free); - link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free); + link->nexthops = set_free(link->nexthops); + link->nexthops_foreign = set_free(link->nexthops_foreign); - link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free); - link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free); + link->neighbors = set_free(link->neighbors); + link->neighbors_foreign = set_free(link->neighbors_foreign); - link->addresses = set_free_with_destructor(link->addresses, address_free); - link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free); + link->addresses = set_free(link->addresses); + link->addresses_foreign = set_free(link->addresses_foreign); + link->static_addresses = set_free(link->static_addresses); + link->dhcp6_addresses = set_free(link->dhcp6_addresses); + link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old); + link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses); + link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old); + link->ndisc_addresses = set_free(link->ndisc_addresses); + link->ndisc_addresses_old = set_free(link->ndisc_addresses_old); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); @@ -752,7 +775,7 @@ int link_get(Manager *m, int ifindex, Link **ret) { Link *link; assert(m); - assert(ifindex); + assert(ifindex > 0); assert(ret); link = hashmap_get(m->links, INT_TO_PTR(ifindex)); @@ -795,8 +818,6 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { assert(link->manager); assert(link->manager->event); - dhcp4_release_old_lease(link); - bool keep_dhcp = may_keep_dhcp && link->network && (link->manager->restarting || @@ -828,6 +849,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) { r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); } + if (link_dhcp6_pd_is_enabled(link)) { + k = dhcp6_pd_remove(link); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); + } + if (link->ndisc) { k = sd_ndisc_stop(link->ndisc); if (k < 0) @@ -927,10 +954,9 @@ static int link_request_set_routing_policy_rule(Link *link) { } routing_policy_rule_purge(link->manager, link); - if (link->routing_policy_rule_messages == 0) { + if (link->routing_policy_rule_messages == 0) link->routing_policy_rules_configured = true; - link_check_ready(link); - } else { + else { log_link_debug(link, "Setting routing policy rules"); link_set_state(link, LINK_STATE_CONFIGURING); } @@ -967,10 +993,12 @@ static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) return 1; } -int link_request_set_nexthop(Link *link) { +static int link_request_set_nexthop(Link *link) { NextHop *nh; int r; + link->static_nexthops_configured = false; + LIST_FOREACH(nexthops, nh, link->network->static_nexthops) { r = nexthop_configure(nh, link, nexthop_handler); if (r < 0) @@ -1013,7 +1041,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { if (link->route_messages == 0) { log_link_debug(link, "Routes set"); link->static_routes_configured = true; - link_check_ready(link); + link_request_set_nexthop(link); } return 1; @@ -1030,12 +1058,12 @@ int link_request_set_routes(Link *link) { assert(link); assert(link->network); - assert(link->addresses_configured); - assert(link->address_messages == 0); assert(link->state != _LINK_STATE_INVALID); link->static_routes_configured = false; - link->static_routes_ready = false; + + if (!link->addresses_ready) + return 0; if (!link_has_carrier(link) && !link->network->configure_without_carrier) /* During configuring addresses, the link lost its carrier. As networkd is dropping @@ -1055,7 +1083,7 @@ int link_request_set_routes(Link *link) { if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY)) continue; - r = route_configure(rt, link, route_handler); + r = route_configure(rt, link, route_handler, NULL); if (r < 0) return log_link_warning_errno(link, r, "Could not set routes: %m"); if (r > 0) @@ -1064,7 +1092,7 @@ int link_request_set_routes(Link *link) { if (link->route_messages == 0) { link->static_routes_configured = true; - link_check_ready(link); + link_request_set_nexthop(link); } else { log_link_debug(link, "Setting routes"); link_set_state(link, LINK_STATE_CONFIGURING); @@ -1076,75 +1104,109 @@ int link_request_set_routes(Link *link) { void link_check_ready(Link *link) { Address *a; Iterator i; - int r; assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + if (link->state == LINK_STATE_CONFIGURED) return; + if (link->state != LINK_STATE_CONFIGURING) { + log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state)); + return; + } + if (!link->network) return; - if (!link->addresses_configured) + if (!link->addresses_configured) { + log_link_debug(link, "%s(): static addresses are not configured.", __func__); return; + } - if (!link->neighbors_configured) + if (!link->neighbors_configured) { + log_link_debug(link, "%s(): static neighbors are not configured.", __func__); return; + } SET_FOREACH(a, link->addresses, i) - if (!address_is_ready(a)) + if (!address_is_ready(a)) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(a->family, &a->in_addr, &str); + log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen); return; + } - if (!link->addresses_ready) { - link->addresses_ready = true; - r = link_request_set_routes(link); - if (r < 0) - link_enter_failed(link); + if (!link->static_routes_configured) { + log_link_debug(link, "%s(): static routes are not configured.", __func__); return; } - if (!link->static_routes_configured) - return; - - if (!link->static_routes_ready) { - link->static_routes_ready = true; - r = link_request_set_nexthop(link); - if (r < 0) - link_enter_failed(link); + if (!link->static_nexthops_configured) { + log_link_debug(link, "%s(): static nexthops are not configured.", __func__); return; } - if (!link->static_nexthops_configured) + if (!link->routing_policy_rules_configured) { + log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__); return; + } - if (!link->routing_policy_rules_configured) + if (!link->tc_configured) { + log_link_debug(link, "%s(): traffic controls are not configured.", __func__); return; + } - if (!link->qdiscs_configured) + if (!link->sr_iov_configured) { + log_link_debug(link, "%s(): SR-IOV is not configured.", __func__); return; + } if (link_has_carrier(link) || !link->network->configure_without_carrier) { - if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address) + if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) { + log_link_debug(link, "%s(): IPv4LL is not configured.", __func__); return; + } if (link_ipv6ll_enabled(link) && - in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) + in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) { + log_link_debug(link, "%s(): IPv6LL is not configured.", __func__); return; + } - if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link)) && - !link->dhcp4_configured && - !link->dhcp6_configured && - !link->ndisc_configured && - !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address)) - /* When DHCP or RA is enabled, at least one protocol must provide an address, or - * an IPv4ll fallback address must be configured. */ + if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && + !link->dhcp_address && set_isempty(link->dhcp6_addresses) && set_isempty(link->ndisc_addresses) && + !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { + log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no dynamic address is assigned yet.", __func__); return; + } + + if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || link_ipv6_accept_ra_enabled(link)) { + if (!link->dhcp4_configured && + !(link->dhcp6_address_configured && link->dhcp6_route_configured) && + !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) && + !(link->ndisc_addresses_configured && link->ndisc_routes_configured) && + !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { + /* When DHCP or RA is enabled, at least one protocol must provide an address, or + * an IPv4ll fallback address must be configured. */ + log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__); + return; + } + + log_link_debug(link, "%s(): dhcp4:%s dhcp6_addresses:%s dhcp_routes:%s dhcp_pd_addresses:%s dhcp_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s", + __func__, + yes_no(link->dhcp4_configured), + yes_no(link->dhcp6_address_configured), + yes_no(link->dhcp6_route_configured), + yes_no(link->dhcp6_pd_address_configured), + yes_no(link->dhcp6_pd_route_configured), + yes_no(link->ndisc_addresses_configured), + yes_no(link->ndisc_routes_configured)); + } } - if (link->state != LINK_STATE_CONFIGURED) - link_enter_configured(link); + link_enter_configured(link); return; } @@ -1176,6 +1238,50 @@ static int link_request_set_neighbors(Link *link) { return 0; } +static int link_set_bridge_fdb(Link *link) { + FdbEntry *fdb_entry; + int r; + + LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { + r = fdb_entry_configure(link, fdb_entry); + if (r < 0) + return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); + } + + return 0; +} + +static int static_address_ready_callback(Address *address) { + Address *a; + Iterator i; + Link *link; + + assert(address); + assert(address->link); + + link = address->link; + + if (!link->addresses_configured) + return 0; + + SET_FOREACH(a, link->static_addresses, i) + if (!address_is_ready(a)) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(a->family, &a->in_addr, &str); + log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen); + return 0; + } + + /* This should not be called again */ + SET_FOREACH(a, link->static_addresses, i) + a->callback = NULL; + + link->addresses_ready = true; + + return link_request_set_routes(link); +} + static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -1201,23 +1307,50 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) (void) manager_rtnl_process_address(rtnl, m, link->manager); if (link->address_messages == 0) { + Address *a; + log_link_debug(link, "Addresses set"); link->addresses_configured = true; - link_check_ready(link); + + /* When all static addresses are already ready, then static_address_ready_callback() + * will not be called automatically. So, call it here. */ + a = set_first(link->static_addresses); + if (!a) { + log_link_warning(link, "No static address is stored."); + link_enter_failed(link); + return 1; + } + if (!a->callback) { + log_link_warning(link, "Address ready callback is not set."); + link_enter_failed(link); + return 1; + } + r = a->callback(a); + if (r < 0) + link_enter_failed(link); } return 1; } -static int link_set_bridge_fdb(Link *link) { - FdbEntry *fdb_entry; +static int static_address_configure(Address *address, Link *link, bool update) { + Address *ret; int r; - LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { - r = fdb_entry_configure(link, fdb_entry); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); - } + assert(address); + assert(link); + + r = address_configure(address, link, address_handler, update, &ret); + if (r < 0) + return log_link_warning_errno(link, r, "Could not configure static address: %m"); + + link->address_messages++; + + r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store static address: %m"); + + ret->callback = static_address_ready_callback; return 0; } @@ -1225,6 +1358,7 @@ static int link_set_bridge_fdb(Link *link) { static int link_request_set_addresses(Link *link) { AddressLabel *label; Address *ad; + Prefix *p; int r; assert(link); @@ -1236,7 +1370,6 @@ static int link_request_set_addresses(Link *link) { link->addresses_ready = false; link->neighbors_configured = false; link->static_routes_configured = false; - link->static_routes_ready = false; link->static_nexthops_configured = false; link->routing_policy_rules_configured = false; @@ -1251,15 +1384,41 @@ static int link_request_set_addresses(Link *link) { LIST_FOREACH(addresses, ad, link->network->static_addresses) { bool update; - update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; + if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer)) + update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0; + else + update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0; - r = address_configure(ad, link, address_handler, update); + r = static_address_configure(ad, link, update); if (r < 0) - return log_link_warning_errno(link, r, "Could not set addresses: %m"); - if (r > 0) - link->address_messages++; + return r; } + if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) + LIST_FOREACH(prefixes, p, link->network->static_prefixes) { + _cleanup_(address_freep) Address *address = NULL; + + if (!p->assign) + continue; + + r = address_new(&address); + if (r < 0) + return log_oom(); + + r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen); + if (r < 0) + return log_link_warning_errno(link, r, "Could not get RA prefix: %m"); + + r = generate_ipv6_eui_64_address(link, &address->in_addr.in6); + if (r < 0) + return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m"); + + address->family = AF_INET6; + r = static_address_configure(address, link, true); + if (r < 0) + return r; + } + LIST_FOREACH(labels, label, link->network->address_labels) { r = address_label_configure(label, link, NULL, false); if (r < 0) @@ -1268,8 +1427,7 @@ static int link_request_set_addresses(Link *link) { link->address_label_messages++; } - /* now that we can figure out a default address for the dhcp server, - start it */ + /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) { r = dhcp4_server_configure(link); if (r < 0) @@ -1279,7 +1437,10 @@ static int link_request_set_addresses(Link *link) { if (link->address_messages == 0) { link->addresses_configured = true; - link_check_ready(link); + link->addresses_ready = true; + r = link_request_set_routes(link); + if (r < 0) + return r; } else { log_link_debug(link, "Setting addresses"); link_set_state(link, LINK_STATE_CONFIGURING); @@ -1526,7 +1687,22 @@ static int link_acquire_ipv6_conf(Link *link) { return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m"); } - (void) dhcp6_request_prefix_delegation(link); + if (link_dhcp6_enabled(link) && IN_SET(link->network->dhcp6_without_ra, + DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST, + DHCP6_CLIENT_START_MODE_SOLICIT)) { + assert(link->dhcp6_client); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + + r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + else + log_link_debug(link, "Acquiring DHCPv6 lease"); + } + + r = dhcp6_request_prefix_delegation(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m"); return 0; } @@ -1654,12 +1830,18 @@ static int link_configure_addrgen_mode(Link *link) { if (!link_ipv6ll_enabled(link)) ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE; - else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0) - /* The file may not exist. And even if it exists, when stable_secret is unset, - * reading the file fails with EIO. */ - ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; - else - ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + else if (link->network->ipv6ll_address_gen_mode < 0) { + r = sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL); + if (r < 0) { + /* The file may not exist. And even if it exists, when stable_secret is unset, + * reading the file fails with EIO. */ + log_link_debug_errno(link, r, "Failed to read sysctl property stable_secret: %m"); + + ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; + } else + ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + } else + ipv6ll_mode = link->network->ipv6ll_address_gen_mode; r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode); if (r < 0) @@ -1787,6 +1969,53 @@ int link_down(Link *link, link_netlink_message_handler_t callback) { return 0; } +static int link_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_message_warning_errno(link, m, r, "Could not set group for the interface"); + + return 1; +} + +static int link_set_group(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(link->manager->rtnl); + + if (link->network->group <= 0) + return 0; + + log_link_debug(link, "Setting group"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_netlink_message_append_u32(req, IFLA_GROUP, link->network->group); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link group: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_group_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); + + return 0; +} + static int link_handle_bound_to_list(Link *link) { Link *l; Iterator i; @@ -2031,11 +2260,7 @@ static int link_append_to_master(Link *link, NetDev *netdev) { if (r < 0) return r; - r = set_ensure_allocated(&master->slaves, NULL); - if (r < 0) - return r; - - r = set_put(master->slaves, link); + r = set_ensure_put(&master->slaves, NULL, link); if (r <= 0) return r; @@ -2438,6 +2663,22 @@ static int link_set_ipv6_mtu(Link *link) { return 0; } +static int link_set_ipv4_accept_local(Link *link) { + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (link->network->ipv4_accept_local < 0) + return 0; + + r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface: %m"); + + return 0; +} + static bool link_is_static_address_configured(Link *link, Address *address) { Address *net_address; @@ -2450,6 +2691,9 @@ static bool link_is_static_address_configured(Link *link, Address *address) { LIST_FOREACH(addresses, net_address, link->network->static_addresses) if (address_equal(net_address, address)) return true; + else if (address->family == AF_INET6 && net_address->family == AF_INET6 && + in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0) + return true; return false; } @@ -2705,24 +2949,46 @@ static int link_configure_ipv4_dad(Link *link) { return 0; } -static int link_configure_qdiscs(Link *link) { - QDisc *qdisc; +static int link_configure_traffic_control(Link *link) { + TrafficControl *tc; Iterator i; int r; - link->qdiscs_configured = false; - link->qdisc_messages = 0; + link->tc_configured = false; + link->tc_messages = 0; - ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) { - r = qdisc_configure(link, qdisc); + ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section, i) { + r = traffic_control_configure(link, tc); if (r < 0) return r; } - if (link->qdisc_messages == 0) - link->qdiscs_configured = true; + if (link->tc_messages == 0) + link->tc_configured = true; else - log_link_debug(link, "Configuring queuing discipline (qdisc)"); + log_link_debug(link, "Configuring traffic control"); + + return 0; +} + +static int link_configure_sr_iov(Link *link) { + SRIOV *sr_iov; + Iterator i; + int r; + + link->sr_iov_configured = false; + link->sr_iov_messages = 0; + + ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section, i) { + r = sr_iov_configure(link, sr_iov); + if (r < 0) + return r; + } + + if (link->sr_iov_messages == 0) + link->sr_iov_configured = true; + else + log_link_debug(link, "Configuring SR-IOV"); return 0; } @@ -2734,7 +3000,11 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_INITIALIZED); - r = link_configure_qdiscs(link); + r = link_configure_traffic_control(link); + if (r < 0) + return r; + + r = link_configure_sr_iov(link); if (r < 0) return r; @@ -2777,10 +3047,18 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_ipv4_accept_local(link); + if (r < 0) + return r; + r = link_set_flags(link); if (r < 0) return r; + r = link_set_group(link); + if (r < 0) + return r; + if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) { r = ipv4ll_configure(link); if (r < 0) @@ -3015,12 +3293,12 @@ static int link_configure_duid(Link *link) { r = set_put(m->links_requesting_uuid, link); if (r < 0) return log_oom(); + if (r > 0) + link_ref(link); r = set_put(m->duids_requesting_uuid, duid); if (r < 0) return log_oom(); - - link_ref(link); } return 0; @@ -3445,6 +3723,10 @@ network_file_fail: if (r < 0) return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m"); + r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0); + if (r < 0) + return log_link_error_errno(link, r, "Failed to attach DHCPv4 event: %m"); + r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in); if (r < 0) return log_link_error_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address); @@ -3463,6 +3745,10 @@ dhcp4_address_fail: if (r < 0) return log_link_error_errno(link, r, "Failed to create IPv4LL client: %m"); + r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); + if (r < 0) + return log_link_error_errno(link, r, "Failed to attach IPv4LL event: %m"); + r = sd_ipv4ll_set_address(link->ipv4ll, &address.in); if (r < 0) return log_link_error_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address); @@ -3740,78 +4026,114 @@ int link_update(Link *link, sd_netlink_message *m) { /* The kernel may broadcast NEWLINK messages without the MAC address set, simply ignore them. */ r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac); - if (r >= 0) { - if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, - ETH_ALEN)) { + if (r >= 0 && memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN) != 0) { - memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, - ETH_ALEN); + memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN); - log_link_debug(link, "MAC address: " - "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - mac.ether_addr_octet[0], - mac.ether_addr_octet[1], - mac.ether_addr_octet[2], - mac.ether_addr_octet[3], - mac.ether_addr_octet[4], - mac.ether_addr_octet[5]); + log_link_debug(link, "Gained new MAC address: " + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + mac.ether_addr_octet[0], + mac.ether_addr_octet[1], + mac.ether_addr_octet[2], + mac.ether_addr_octet[3], + mac.ether_addr_octet[4], + mac.ether_addr_octet[5]); - if (link->ipv4ll) { - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (link->ipv4ll) { + bool restart = sd_ipv4ll_is_running(link->ipv4ll) > 0; + + if (restart) { + r = sd_ipv4ll_stop(link->ipv4ll); if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); + return log_link_warning_errno(link, r, "Could not stop IPv4LL client: %m"); } - if (link->dhcp_client) { - r = sd_dhcp_client_set_mac(link->dhcp_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); - r = dhcp4_set_client_identifier(link); + if (restart) { + r = sd_ipv4ll_start(link->ipv4ll); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Could not restart IPv4LL client: %m"); + } + } + + if (link->dhcp_client) { + r = sd_dhcp_client_set_mac(link->dhcp_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); + + r = dhcp4_set_client_identifier(link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set DHCP client identifier: %m"); + } + + if (link->dhcp6_client) { + const DUID* duid = link_get_duid(link); + bool restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0; + + if (restart) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); } - if (link->dhcp6_client) { - const DUID* duid = link_get_duid(link); + r = sd_dhcp6_client_set_mac(link->dhcp6_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - r = sd_dhcp6_client_set_mac(link->dhcp6_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); + if (link->network->iaid_set) { + r = sd_dhcp6_client_set_iaid(link->dhcp6_client, link->network->iaid); if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - - if (link->network->iaid_set) { - r = sd_dhcp6_client_set_iaid(link->dhcp6_client, - link->network->iaid); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); - } - - r = sd_dhcp6_client_set_duid(link->dhcp6_client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); + return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); } - if (link->radv) { - r = sd_radv_set_mac(link->radv, &link->mac); + r = sd_dhcp6_client_set_duid(link->dhcp6_client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); + + if (restart) { + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m"); + return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m"); + } + } + + if (link->radv) { + bool restart = sd_radv_is_running(link->radv); + + if (restart) { + r = sd_radv_stop(link->radv); + if (r < 0) + return log_link_warning_errno(link, r, "Could not stop Router Advertisement: %m"); } - if (link->ndisc) { - r = sd_ndisc_set_mac(link->ndisc, &link->mac); + r = sd_radv_set_mac(link->radv, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m"); + + if (restart) { + r = sd_radv_start(link->radv); if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC for ndisc: %m"); + return log_link_warning_errno(link, r, "Could not restart Router Advertisement: %m"); } } + + if (link->ndisc) { + r = sd_ndisc_set_mac(link->ndisc, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC for NDisc: %m"); + } } old_master = link->master_ifindex; @@ -3880,32 +4202,80 @@ static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { fputc('\n', f); } -static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) { - unsigned j; - int r; +static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) { + for (unsigned j = 0; j < n_dns; j++) { + const char *str; - for (j = 0; j < n_dns; j++) { - _cleanup_free_ char *b = NULL; - - r = in_addr_to_string(dns[j].family, &dns[j].address, &b); - if (r < 0) { - log_debug_errno(r, "Failed to format address, ignoring: %m"); + if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex) + continue; + + str = in_addr_full_to_string(dns[j]); + if (!str) continue; - } if (*space) fputc(' ', f); - fputs(b, f); + fputs(str, f); *space = true; } } +static void serialize_addresses( + FILE *f, + const char *lvalue, + bool *space, + char **addresses, + sd_dhcp_lease *lease, + bool conditional, + sd_dhcp_lease_server_type what, + sd_dhcp6_lease *lease6, + bool conditional6, + int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**), + int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) { + int r; + + bool _space = false; + if (!space) + space = &_space; + + if (lvalue) + fprintf(f, "%s=", lvalue); + fputstrv(f, addresses, NULL, space); + + if (lease && conditional) { + const struct in_addr *lease_addresses; + + r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses); + if (r > 0) + serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local); + } + + if (lease6 && conditional6 && lease6_get_addr) { + const struct in6_addr *in6_addrs; + + r = lease6_get_addr(lease6, &in6_addrs); + if (r > 0) + serialize_in6_addrs(f, in6_addrs, r, space); + } + + if (lease6 && conditional6 && lease6_get_fqdn) { + char **in6_hosts; + + r = lease6_get_fqdn(lease6, &in6_hosts); + if (r > 0) + fputstrv(f, in6_hosts, NULL, space); + } + + if (lvalue) + fputc('\n', f); +} + int link_save(Link *link) { + const char *admin_state, *oper_state, *carrier_state, *address_state; _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *admin_state, *oper_state, *carrier_state, *address_state; - Address *a; Route *route; + Address *a; Iterator i; int r; @@ -3950,133 +4320,78 @@ int link_save(Link *link) { if (link->network) { char **dhcp6_domains = NULL, **dhcp_domains = NULL; const char *dhcp_domainname = NULL, *p; - sd_dhcp6_lease *dhcp6_lease = NULL; bool space; fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", yes_no(link->network->required_for_online)); - fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s", - strempty(link_operstate_to_string(link->network->required_operstate_for_online.min))); - - if (link->network->required_operstate_for_online.max != LINK_OPERSTATE_RANGE_DEFAULT.max) - fprintf(f, ":%s", - strempty(link_operstate_to_string(link->network->required_operstate_for_online.max))); - - fprintf(f, "\n"); - - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); - if (r < 0 && r != -ENOMSG) - log_link_debug_errno(link, r, "Failed to get DHCPv6 lease: %m"); - } + LinkOperationalStateRange st = link->network->required_operstate_for_online; + fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n", + strempty(link_operstate_to_string(st.min)), + st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "", + st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : ""); fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); + /************************************************************/ + fputs("DNS=", f); space = false; - if (link->n_dns != (unsigned) -1) - link_save_dns(f, link->dns, link->n_dns, &space); + link_save_dns(link, f, link->dns, link->n_dns, &space); else - link_save_dns(f, link->network->dns, link->network->n_dns, &space); + link_save_dns(link, f, link->network->dns, link->network->n_dns, &space); - if (link->network->dhcp_use_dns && - link->dhcp_lease) { - const struct in_addr *addresses; + serialize_addresses(f, NULL, &space, + NULL, + link->dhcp_lease, + link->network->dhcp_use_dns, + SD_DHCP_LEASE_DNS, + link->dhcp6_lease, + link->network->dhcp6_use_dns, + sd_dhcp6_lease_get_dns, + NULL); - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); - if (r > 0) - if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0) - space = true; - } - - if (link->network->dhcp6_use_dns && dhcp6_lease) { - struct in6_addr *in6_addrs; - - r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in6_addrs(f, in6_addrs, r); - space = true; - } - } - - /* Make sure to flush out old entries before we use the NDISC data */ + /* Make sure to flush out old entries before we use the NDisc data */ ndisc_vacuum(link); if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) { NDiscRDNSS *dd; - SET_FOREACH(dd, link->ndisc_rdnss, i) { - if (space) - fputc(' ', f); - - serialize_in6_addrs(f, &dd->address, 1); - space = true; - } + SET_FOREACH(dd, link->ndisc_rdnss, i) + serialize_in6_addrs(f, &dd->address, 1, &space); } fputc('\n', f); - fputs("NTP=", f); - space = false; - fputstrv(f, link->ntp ?: link->network->ntp, NULL, &space); + /************************************************************/ - if (link->network->dhcp_use_ntp && - link->dhcp_lease) { - const struct in_addr *addresses; + serialize_addresses(f, "NTP", NULL, + link->ntp ?: link->network->ntp, + link->dhcp_lease, + link->network->dhcp_use_ntp, + SD_DHCP_LEASE_NTP, + link->dhcp6_lease, + link->network->dhcp6_use_ntp, + sd_dhcp6_lease_get_ntp_addrs, + sd_dhcp6_lease_get_ntp_fqdn); - r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); - if (r > 0) - if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0) - space = true; - } + serialize_addresses(f, "SIP", NULL, + NULL, + link->dhcp_lease, + link->network->dhcp_use_sip, + SD_DHCP_LEASE_SIP, + NULL, false, NULL, NULL); - fputc('\n', f); - - fputs("SIP=", f); - space = false; - fputstrv(f, link->network->sip, NULL, &space); - - if (link->network->dhcp_use_sip && - link->dhcp_lease) { - const struct in_addr *addresses; - - r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses); - if (r > 0) - if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0) - space = true; - } - - if (link->network->dhcp6_use_ntp && dhcp6_lease) { - struct in6_addr *in6_addrs; - char **hosts; - - r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease, - &in6_addrs); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in6_addrs(f, in6_addrs, r); - space = true; - } - - r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts); - if (r > 0) - fputstrv(f, hosts, NULL, &space); - } - - fputc('\n', f); + /************************************************************/ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { if (link->dhcp_lease) { (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains); } - if (dhcp6_lease) - (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); + if (link->dhcp6_lease) + (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains); } fputs("DOMAINS=", f); @@ -4102,6 +4417,8 @@ int link_save(Link *link) { fputc('\n', f); + /************************************************************/ + fputs("ROUTE_DOMAINS=", f); space = false; ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains, i) @@ -4125,47 +4442,58 @@ int link_save(Link *link) { fputc('\n', f); + /************************************************************/ + fprintf(f, "LLMNR=%s\n", resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr)); + + /************************************************************/ + fprintf(f, "MDNS=%s\n", resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns)); - if (link->dns_default_route >= 0) - fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->dns_default_route)); - else if (link->network->dns_default_route >= 0) - fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route)); - if (link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID) - fprintf(f, "DNS_OVER_TLS=%s\n", - dns_over_tls_mode_to_string(link->dns_over_tls_mode)); - else if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID) - fprintf(f, "DNS_OVER_TLS=%s\n", - dns_over_tls_mode_to_string(link->network->dns_over_tls_mode)); + /************************************************************/ - if (link->dnssec_mode != _DNSSEC_MODE_INVALID) - fprintf(f, "DNSSEC=%s\n", - dnssec_mode_to_string(link->dnssec_mode)); - else if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID) - fprintf(f, "DNSSEC=%s\n", - dnssec_mode_to_string(link->network->dnssec_mode)); + int dns_default_route = + link->dns_default_route >= 0 ? link->dns_default_route : + link->network->dns_default_route; + if (dns_default_route >= 0) + fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route)); - if (!set_isempty(link->dnssec_negative_trust_anchors)) { + /************************************************************/ + + DnsOverTlsMode dns_over_tls_mode = + link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode : + link->network->dns_over_tls_mode; + if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID) + fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode)); + + /************************************************************/ + + DnssecMode dnssec_mode = + link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode : + link->network->dnssec_mode; + if (dnssec_mode != _DNSSEC_MODE_INVALID) + fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode)); + + /************************************************************/ + + Set *nta_anchors = link->dnssec_negative_trust_anchors; + if (set_isempty(nta_anchors)) + nta_anchors = link->network->dnssec_negative_trust_anchors; + + if (!set_isempty(nta_anchors)) { const char *n; fputs("DNSSEC_NTA=", f); space = false; - SET_FOREACH(n, link->dnssec_negative_trust_anchors, i) - fputs_with_space(f, n, NULL, &space); - fputc('\n', f); - } else if (!set_isempty(link->network->dnssec_negative_trust_anchors)) { - const char *n; - - fputs("DNSSEC_NTA=", f); - space = false; - SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i) + SET_FOREACH(n, nta_anchors, i) fputs_with_space(f, n, NULL, &space); fputc('\n', f); } + /************************************************************/ + fputs("ADDRESSES=", f); space = false; SET_FOREACH(a, link->addresses, i) { @@ -4180,6 +4508,8 @@ int link_save(Link *link) { } fputc('\n', f); + /************************************************************/ + fputs("ROUTES=", f); space = false; SET_FOREACH(route, link->routes, i) { @@ -4202,22 +4532,6 @@ int link_save(Link *link) { print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); if (link->dhcp_lease) { - struct in_addr address; - const char *tz = NULL; - - assert(link->network); - - r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); - if (r >= 0) - fprintf(f, "TIMEZONE=%s\n", tz); - - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); - if (r >= 0) { - fputs("DHCP4_ADDRESS=", f); - serialize_in_addrs(f, &address, 1, false, NULL); - fputc('\n', f); - } - r = dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) goto fail; @@ -4239,6 +4553,19 @@ int link_save(Link *link) { } } + if (link->dhcp6_client) { + _cleanup_free_ char *duid = NULL; + uint32_t iaid; + + r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid); + if (r >= 0) + fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid); + + r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid); + if (r >= 0) + fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid); + } + r = fflush_and_check(f); if (r < 0) goto fail; @@ -4267,16 +4594,10 @@ void link_dirty(Link *link) { /* mark manager dirty as link is dirty */ manager_dirty(link->manager); - r = set_ensure_allocated(&link->manager->dirty_links, NULL); - if (r < 0) - /* allocation errors are ignored */ - return; - - r = set_put(link->manager->dirty_links, link); + r = set_ensure_put(&link->manager->dirty_links, NULL, link); if (r <= 0) - /* don't take another ref if the link was already dirty */ + /* Ignore allocation errors and don't take another ref if the link was already dirty */ return; - link_ref(link); } @@ -4288,6 +4609,17 @@ void link_clean(Link *link) { link_unref(set_remove(link->manager->dirty_links, link)); } +int link_save_and_clean(Link *link) { + int r; + + r = link_save(link); + if (r < 0) + return r; + + link_clean(link); + return 0; +} + static const char* const link_state_table[_LINK_STATE_MAX] = { [LINK_STATE_PENDING] = "pending", [LINK_STATE_INITIALIZED] = "initialized", diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7494f88b8..7f99c0f47 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -81,11 +81,13 @@ typedef struct Link { unsigned nexthop_messages; unsigned routing_policy_rule_messages; unsigned routing_policy_rule_remove_messages; - unsigned qdisc_messages; + unsigned tc_messages; + unsigned sr_iov_messages; unsigned enslaving; Set *addresses; Set *addresses_foreign; + Set *static_addresses; Set *neighbors; Set *neighbors_foreign; Set *routes; @@ -94,30 +96,29 @@ typedef struct Link { Set *nexthops_foreign; sd_dhcp_client *dhcp_client; - sd_dhcp_lease *dhcp_lease, *dhcp_lease_old; - Set *dhcp_routes; + sd_dhcp_lease *dhcp_lease; + Address *dhcp_address, *dhcp_address_old; + Set *dhcp_routes, *dhcp_routes_old; char *lease_file; uint32_t original_mtu; unsigned dhcp4_messages; + unsigned dhcp4_remove_messages; bool dhcp4_route_failed:1; bool dhcp4_route_retrying:1; bool dhcp4_configured:1; - bool dhcp6_configured:1; - - unsigned ndisc_messages; - bool ndisc_configured; + bool dhcp4_address_bind:1; sd_ipv4ll *ipv4ll; - bool ipv4ll_address:1; + bool ipv4ll_address_configured:1; bool addresses_configured:1; bool addresses_ready:1; bool neighbors_configured:1; bool static_routes_configured:1; - bool static_routes_ready:1; bool static_nexthops_configured:1; bool routing_policy_rules_configured:1; - bool qdiscs_configured:1; + bool tc_configured:1; + bool sr_iov_configured:1; bool setting_mtu:1; bool setting_genmode:1; bool ipv6_mtu_set:1; @@ -129,10 +130,30 @@ typedef struct Link { sd_ndisc *ndisc; Set *ndisc_rdnss; Set *ndisc_dnssl; + Set *ndisc_addresses, *ndisc_addresses_old; + Set *ndisc_routes, *ndisc_routes_old; + unsigned ndisc_addresses_messages; + unsigned ndisc_routes_messages; + bool ndisc_addresses_configured:1; + bool ndisc_routes_configured:1; sd_radv *radv; sd_dhcp6_client *dhcp6_client; + sd_dhcp6_lease *dhcp6_lease; + Set *dhcp6_addresses, *dhcp6_addresses_old; + Set *dhcp6_routes, *dhcp6_routes_old; + Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old; + Set *dhcp6_pd_routes, *dhcp6_pd_routes_old; + unsigned dhcp6_address_messages; + unsigned dhcp6_route_messages; + unsigned dhcp6_pd_address_messages; + unsigned dhcp6_pd_route_messages; + bool dhcp6_address_configured:1; + bool dhcp6_route_configured:1; + bool dhcp6_pd_address_configured:1; + bool dhcp6_pd_route_configured:1; + bool dhcp6_pd_prefixes_assigned:1; /* This is about LLDP reception */ sd_lldp *lldp; @@ -150,8 +171,8 @@ typedef struct Link { struct rtnl_link_stats64 stats_old, stats_new; bool stats_updated; - /* All kinds of DNS configuration */ - struct in_addr_data *dns; + /* All kinds of DNS configuration the user configured via D-Bus */ + struct in_addr_full **dns; unsigned n_dns; OrderedSet *search_domains, *route_domains; @@ -162,6 +183,7 @@ typedef struct Link { DnsOverTlsMode dns_over_tls_mode; Set *dnssec_negative_trust_anchors; + /* Similar, but NTP server configuration */ char **ntp; } Link; @@ -195,6 +217,7 @@ int link_update(Link *link, sd_netlink_message *message); void link_dirty(Link *link); void link_clean(Link *link); int link_save(Link *link); +int link_save_and_clean(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); @@ -214,7 +237,6 @@ uint32_t link_get_vrf_table(Link *link); uint32_t link_get_dhcp_route_table(Link *link); uint32_t link_get_ipv6_accept_ra_route_table(Link *link); int link_request_set_routes(Link *link); -int link_request_set_nexthop(Link *link); int link_reconfigure(Link *link, bool force); diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 93ab2144a..9e0b44752 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -20,6 +20,7 @@ #include "string-util.h" #include "strv.h" #include "unaligned.h" +#include "web-util.h" /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ #define LLDP_TX_FAST_INIT 4U @@ -86,9 +87,11 @@ static int lldp_make_packet( const char *pretty_hostname, uint16_t system_capabilities, uint16_t enabled_capabilities, + char *mud, void **ret, size_t *sz) { - size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0; + size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, + pretty_hostname_length = 0, mud_length = 0; _cleanup_free_ void *packet = NULL; struct ether_header *h; uint8_t *p; @@ -115,6 +118,9 @@ static int lldp_make_packet( if (pretty_hostname) pretty_hostname_length = strlen(pretty_hostname); + if (mud) + mud_length = strlen(mud); + l = sizeof(struct ether_header) + /* Chassis ID */ 2 + 1 + machine_id_length + @@ -139,6 +145,10 @@ static int lldp_make_packet( if (pretty_hostname) l += 2 + pretty_hostname_length; + /* MUD URL */ + if (mud) + l += 2 + sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length; + packet = malloc(l); if (!packet) return -ENOMEM; @@ -189,6 +199,32 @@ static int lldp_make_packet( p = mempcpy(p, pretty_hostname, pretty_hostname_length); } + if (mud) { + uint8_t oui_mud[sizeof(SD_LLDP_OUI_MUD)] = {0x00, 0x00, 0x5E}; + /* + * +--------+--------+----------+---------+-------------- + * |TLV Type| len | OUI |subtype | MUDString + * | =127 | |= 00 00 5E| = 1 | + * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets) + * +--------+--------+----------+---------+-------------- + * where: + + * o TLV Type = 127 indicates a vendor-specific TLV + * o len = indicates the TLV string length + * o OUI = 00 00 5E is the organizationally unique identifier of IANA + * o subtype = 1 (as assigned by IANA for the MUDstring) + * o MUDstring = the length MUST NOT exceed 255 octets + */ + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PRIVATE, sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length); + if (r < 0) + return r; + + p = mempcpy(p, &oui_mud, sizeof(SD_LLDP_OUI_MUD)); + *(p++) = SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION; + p = mempcpy(p, mud, mud_length); + } + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); if (r < 0) return r; @@ -286,6 +322,7 @@ static int link_send_lldp(Link *link) { pretty_hostname, SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, caps, + link->network ? link->network->lldp_mud : NULL, &packet, &packet_size); if (r < 0) return r; @@ -410,7 +447,7 @@ int config_parse_lldp_emit( else { r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); return 0; } @@ -419,3 +456,40 @@ int config_parse_lldp_emit( return 0; } + +int config_parse_lldp_mud( + 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 *unescaped = NULL; + Network *n = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = cunescape(rvalue, 0, &unescaped); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to Failed to unescape LLDP MUD URL, ignoring: %s", rvalue); + return 0; + } + + if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse LLDP MUD URL '%s', ignoring: %m", rvalue); + + return 0; + } + + return free_and_replace(n->lldp_mud, unescaped); +} diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index 561becda4..1409984ac 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -20,3 +20,4 @@ int link_lldp_emit_start(Link *link); void link_lldp_emit_stop(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_lldp_emit); +CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mud); diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c index b7279a582..9db59d93f 100644 --- a/src/network/networkd-manager-bus.c +++ b/src/network/networkd-manager-bus.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-message-util.h" #include "bus-polkit.h" #include "networkd-link-bus.h" #include "networkd-link.h" @@ -93,17 +94,16 @@ static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; Manager *manager = userdata; - int32_t index; + int ifindex, r; Link *link; - int r; - r = sd_bus_message_read(message, "i", &index); + r = bus_message_read_ifindex(message, error, &ifindex); if (r < 0) return r; - link = hashmap_get(manager->links, INT_TO_PTR((int) index)); + link = hashmap_get(manager->links, INT_TO_PTR(ifindex)); if (!link) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %" PRIi32 " not known", index); + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -128,14 +128,10 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_ assert(message); assert(handler); - assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(message, "i", &ifindex); + r = bus_message_read_ifindex(message, error, &ifindex); if (r < 0) return r; - if (ifindex <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); if (!l) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); @@ -151,6 +147,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); } +static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error); +} + static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { return call_link_method(userdata, message, bus_link_method_set_domains, error); } @@ -191,6 +191,10 @@ static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus return call_link_method(userdata, message, bus_link_method_renew, error); } +static int bus_method_force_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_force_renew, error); +} + static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { return call_link_method(userdata, message, bus_link_method_reconfigure, error); } @@ -239,6 +243,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetLinkByIndex", "i", "so", method_get_link_by_index, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkNTP", "ias", NULL, bus_method_set_link_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLinkDNSEx", "ia(iayqs)", NULL, bus_method_set_link_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), @@ -249,6 +254,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ForceRenewLink", "i", NULL, bus_method_force_renew_link, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 24bb72aee..a6c1a39e2 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -11,6 +11,7 @@ #include "sd-netlink.h" #include "alloc-util.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "bus-util.h" #include "conf-parser.h" @@ -23,6 +24,7 @@ #include "local-addresses.h" #include "netlink-util.h" #include "network-internal.h" +#include "networkd-dhcp-server-bus.h" #include "networkd-dhcp6.h" #include "networkd-link-bus.h" #include "networkd-manager-bus.h" @@ -30,6 +32,7 @@ #include "networkd-network-bus.h" #include "networkd-speed-meter.h" #include "ordered-set.h" +#include "path-lookup.h" #include "path-util.h" #include "set.h" #include "signal-util.h" @@ -101,7 +104,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b r = sd_bus_message_read(message, "b", &b); if (r < 0) { - log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); + bus_log_parse_error(r); return 0; } @@ -152,6 +155,10 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add link object vtable: %m"); + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.DHCPServer", dhcp_server_vtable, link_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add link object vtable: %m"); + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m); if (r < 0) return log_error_errno(r, "Failed to add link enumerator: %m"); @@ -164,6 +171,10 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add network enumerator: %m"); + r = bus_log_control_api_register(m->bus); + if (r < 0) + return r; + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.network1", 0, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to request name: %m"); @@ -230,7 +241,7 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi return 0; } if (r > 0) { - log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed: %m"); + log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed."); return 0; } @@ -494,7 +505,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo log_link_debug(link, "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s", - type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering", + (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" : + type == RTM_DELROUTE ? "Forgetting" : + route ? "Received remembered" : "Remembering", strna(buf_dst), strempty(buf_dst_prefixlen), strna(buf_src), strna(buf_gw), strna(buf_prefsrc), format_route_scope(tmp->scope, buf_scope, sizeof buf_scope), @@ -505,7 +518,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo switch (type) { case RTM_NEWROUTE: - if (!route) { + if (!route && link->manager->manage_foreign_routes) { /* A route appeared that we did not request */ r = route_add_foreign(link, tmp, &route); if (r < 0) { @@ -844,8 +857,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, valid_str ? "for " : "forever", strempty(valid_str)); } - /* address_update() logs internally, so we don't need to. */ - (void) address_update(address, flags, scope, &cinfo); + /* address_update() logs internally, so we don't need to here. */ + r = address_update(address, flags, scope, &cinfo); + if (r < 0) + link_enter_failed(link); break; @@ -1260,6 +1275,9 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, if (r < 0 && r != -ENODATA) { log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m"); return 0; + } else if (tmp->oif <= 0) { + log_warning("rtnl: received nexthop message with invalid ifindex %d, ignoring.", tmp->oif); + return 0; } r = link_get(m, tmp->oif, &link); @@ -1409,33 +1427,36 @@ static int manager_connect_rtnl(Manager *m) { return 0; } -static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) { - char *p; +static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) { + const char *p; int r; assert(s); - assert(address); + assert(dns); - r = in_addr_to_string(address->family, &address->address, &p); - if (r < 0) - return r; + if (dns->ifindex != 0 && dns->ifindex != ifindex) + return 0; - r = ordered_set_consume(s, p); + p = in_addr_full_to_string(dns); + if (!p) + return 0; + + r = ordered_set_put_strdup(s, p); if (r == -EEXIST) return 0; return r; } -static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) { +static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) { int r, c = 0; unsigned i; assert(s); - assert(addresses || n == 0); + assert(dns || n == 0); for (i = 0; i < n; i++) { - r = ordered_set_put_in_addr_data(s, addresses+i); + r = ordered_set_put_dns_server(s, ifindex, dns[i]); if (r < 0) return r; @@ -1511,8 +1532,8 @@ static int manager_save(Manager *m) { if (!ntp) return -ENOMEM; - sip = ordered_set_new(&string_hash_ops); - if (!sip) + sip = ordered_set_new(&string_hash_ops); + if (!sip) return -ENOMEM; search_domains = ordered_set_new(&dns_name_hash_ops); @@ -1524,6 +1545,8 @@ static int manager_save(Manager *m) { return -ENOMEM; HASHMAP_FOREACH(link, m->links, i) { + const struct in_addr *addresses; + if (link->flags & IFF_LOOPBACK) continue; @@ -1540,7 +1563,10 @@ static int manager_save(Manager *m) { continue; /* First add the static configured entries */ - r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns); + if (link->n_dns != (unsigned) -1) + r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns); + else + r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns); if (r < 0) return r; @@ -1561,8 +1587,6 @@ static int manager_save(Manager *m) { /* Secondly, add the entries acquired via DHCP */ if (link->network->dhcp_use_dns) { - const struct in_addr *addresses; - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); if (r > 0) { r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local); @@ -1573,8 +1597,6 @@ static int manager_save(Manager *m) { } if (link->network->dhcp_use_ntp) { - const struct in_addr *addresses; - r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); if (r > 0) { r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local); @@ -1585,8 +1607,6 @@ static int manager_save(Manager *m) { } if (link->network->dhcp_use_sip) { - const struct in_addr *addresses; - r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses); if (r > 0) { r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local); @@ -1709,8 +1729,7 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) { manager_save(m); SET_FOREACH(link, m->dirty_links, i) - if (link_save(link) >= 0) - link_clean(link); + (void) link_save_and_clean(link); return 1; } @@ -1747,6 +1766,7 @@ int manager_new(Manager **ret) { *m = (Manager) { .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL, + .manage_foreign_routes = true, .ethtool_fd = -1, }; @@ -1803,27 +1823,20 @@ int manager_new(Manager **ret) { } void manager_free(Manager *m) { - struct in6_addr *a; AddressPool *pool; Link *link; + Iterator i; if (!m) return; free(m->state_file); - while ((a = hashmap_first_key(m->dhcp6_prefixes))) - (void) dhcp6_prefix_remove(m, a); - m->dhcp6_prefixes = hashmap_free(m->dhcp6_prefixes); - - while ((link = hashmap_steal_first(m->links))) { - if (link->dhcp6_client) - (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link); - + HASHMAP_FOREACH(link, m->links, i) (void) link_stop_clients(link, true); - link_unref(link); - } + m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free); + m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free); m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref); @@ -1839,9 +1852,9 @@ void manager_free(Manager *m) { /* routing_policy_rule_free() access m->rules and m->rules_foreign. * So, it is necessary to set NULL after the sets are freed. */ - m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free); - m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free); - set_free_with_destructor(m->rules_saved, routing_policy_rule_free); + m->rules = set_free(m->rules); + m->rules_foreign = set_free(m->rules_foreign); + set_free(m->rules_saved); sd_netlink_unref(m->rtnl); sd_netlink_unref(m->genl); @@ -1880,7 +1893,7 @@ int manager_start(Manager *m) { manager_save(m); HASHMAP_FOREACH(link, m->links, i) - link_save(link); + (void) link_save(link); return 0; } @@ -2019,6 +2032,9 @@ int manager_rtnl_enumerate_routes(Manager *m) { assert(m); assert(m->rtnl); + if (!m->manage_foreign_routes) + return 0; + r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0); if (r < 0) return r; @@ -2300,23 +2316,15 @@ int manager_request_product_uuid(Manager *m, Link *link) { assert_se(duid = link_get_duid(link)); - r = set_ensure_allocated(&m->links_requesting_uuid, NULL); + r = set_ensure_put(&m->links_requesting_uuid, NULL, link); if (r < 0) return log_oom(); + if (r > 0) + link_ref(link); - r = set_ensure_allocated(&m->duids_requesting_uuid, NULL); + r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid); if (r < 0) return log_oom(); - - r = set_put(m->links_requesting_uuid, link); - if (r < 0) - return log_oom(); - - r = set_put(m->duids_requesting_uuid, duid); - if (r < 0) - return log_oom(); - - link_ref(link); } if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 8fdb38480..f4b37bd6a 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -31,6 +31,7 @@ struct Manager { bool enumerating:1; bool dirty:1; bool restarting:1; + bool manage_foreign_routes; Set *dirty_links; @@ -43,6 +44,7 @@ struct Manager { Hashmap *netdevs; OrderedHashmap *networks; Hashmap *dhcp6_prefixes; + Set *dhcp6_pd_prefixes; LIST_HEAD(AddressPool, address_pools); usec_t network_dirs_ts_usec; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index c45fec543..349f0548a 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -13,6 +13,7 @@ #include "networkd-manager.h" #include "networkd-ndisc.h" #include "networkd-route.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" @@ -34,6 +35,279 @@ #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e) +static int ndisc_remove_old(Link *link, bool force); + +static int ndisc_address_callback(Address *address) { + Address *a; + Iterator i; + + assert(address); + assert(address->link); + + /* Make this called only once */ + SET_FOREACH(a, address->link->ndisc_addresses, i) + a->callback = NULL; + + return ndisc_remove_old(address->link, true); +} + +static int ndisc_remove_old(Link *link, bool force) { + Address *address; + Route *route; + NDiscDNSSL *dnssl; + NDiscRDNSS *rdnss; + Iterator i; + int k, r = 0; + + assert(link); + + if (!force) { + bool set_callback = !set_isempty(link->ndisc_addresses); + + if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) + return 0; + + SET_FOREACH(address, link->ndisc_addresses, i) + if (address_is_ready(address)) { + set_callback = false; + break; + } + + if (set_callback) { + SET_FOREACH(address, link->ndisc_addresses, i) + address->callback = ndisc_address_callback; + return 0; + } + } + + if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old)) + log_link_debug(link, "Removing old NDisc addresses and routes."); + + link_dirty(link); + + SET_FOREACH(address, link->ndisc_addresses_old, i) { + k = address_remove(address, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(route, link->ndisc_routes_old, i) { + k = route_remove(route, link, NULL); + if (k < 0) + r = k; + } + + SET_FOREACH(rdnss, link->ndisc_rdnss, i) + if (rdnss->marked) + free(set_remove(link->ndisc_rdnss, rdnss)); + + SET_FOREACH(dnssl, link->ndisc_dnssl, i) + if (dnssl->marked) + free(set_remove(link->ndisc_dnssl, dnssl)); + + return r; +} + +static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->ndisc_routes_messages > 0); + + link->ndisc_routes_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_error_errno(link, m, r, "Could not set NDisc route"); + link_enter_failed(link); + return 1; + } + + if (link->ndisc_routes_messages == 0) { + log_link_debug(link, "NDisc routes set."); + link->ndisc_routes_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + link_check_ready(link); + } + + return 1; +} + +static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->ndisc_addresses_messages > 0); + + link->ndisc_addresses_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_error_errno(link, m, r, "Could not set NDisc address"); + link_enter_failed(link); + return 1; + } else if (r >= 0) + (void) manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->ndisc_addresses_messages == 0) { + log_link_debug(link, "NDisc SLAAC addresses set."); + link->ndisc_addresses_configured = true; + + r = ndisc_remove_old(link, false); + if (r < 0) { + link_enter_failed(link); + return 1; + } + + r = link_request_set_routes(link); + if (r < 0) { + link_enter_failed(link); + return 1; + } + } + + return 1; +} + +static int ndisc_route_configure(Route *route, Link *link) { + Route *ret; + int r; + + assert(route); + assert(link); + + r = route_configure(route, link, ndisc_route_handler, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc route: %m"); + + link->ndisc_routes_messages++; + + r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc route: %m"); + + (void) set_remove(link->ndisc_routes_old, ret); + + return 0; +} + +static int ndisc_address_configure(Address *address, Link *link) { + Address *ret; + int r; + + assert(address); + assert(link); + + r = address_configure(address, link, ndisc_address_handler, true, &ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m"); + + link->ndisc_addresses_messages++; + + r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m"); + + (void) set_remove(link->ndisc_addresses_old, ret); + + return 0; +} + +static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { + _cleanup_(route_freep) Route *route = NULL; + union in_addr_union gateway; + uint16_t lifetime; + unsigned preference; + uint32_t mtu; + usec_t time_now; + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_get_lifetime(rt, &lifetime); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); + + if (lifetime == 0) /* not a default router */ + return 0; + + r = sd_ndisc_router_get_address(rt, &gateway.in6); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); + + if (address_exists(link, AF_INET6, &gateway)) { + if (DEBUG_LOGGING) { + _cleanup_free_ char *buffer = NULL; + + (void) in_addr_to_string(AF_INET6, &gateway, &buffer); + log_link_debug(link, "No NDisc route added, gateway %s matches local address", + strnull(buffer)); + } + return 0; + } + + r = sd_ndisc_router_get_preference(rt, &preference); + 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(), &time_now); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); + + r = sd_ndisc_router_get_mtu(rt, &mtu); + if (r == -ENODATA) + mtu = 0; + else if (r < 0) + return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m"); + + r = route_new(&route); + if (r < 0) + return log_oom(); + + route->family = AF_INET6; + route->table = link_get_ipv6_accept_ra_route_table(link); + route->priority = link->network->dhcp6_route_metric; + route->protocol = RTPROT_RA; + route->pref = preference; + route->gw = gateway; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + route->mtu = mtu; + + r = ndisc_route_configure(route, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set default route: %m"); + + Route *route_gw; + LIST_FOREACH(routes, route_gw, link->network->static_routes) { + if (!route_gw->gateway_from_dhcp) + continue; + + if (route_gw->family != AF_INET6) + continue; + + route_gw->gw = gateway; + + r = ndisc_route_configure(route_gw, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set gateway: %m"); + } + + return 0; +} + static bool stableprivate_address_is_valid(const struct in6_addr *addr) { assert(addr); @@ -51,7 +325,8 @@ static bool stableprivate_address_is_valid(const struct in6_addr *addr) { return true; } -static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr *addr) { +static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) { + _cleanup_free_ struct in6_addr *addr = NULL; sd_id128_t secret_key; struct siphash state; uint64_t rid; @@ -69,194 +344,30 @@ static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, l = MAX(DIV_ROUND_UP(prefix_len, 8), 8); siphash24_compress(prefix, l, &state); - siphash24_compress(link->ifname, strlen(link->ifname), &state); + siphash24_compress_string(link->ifname, &state); siphash24_compress(&link->mac, sizeof(struct ether_addr), &state); siphash24_compress(&dad_counter, sizeof(uint8_t), &state); rid = htole64(siphash24_finalize(&state)); + addr = new(struct in6_addr, 1); + if (!addr) + return log_oom(); + memcpy(addr->s6_addr, prefix->s6_addr, l); - memcpy((uint8_t *) &addr->s6_addr + l, &rid, 16 - l); + memcpy(addr->s6_addr + l, &rid, 16 - l); - return 0; -} - -static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->ndisc_messages > 0); - - link->ndisc_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_error_errno(link, m, r, "Could not set NDisc route or address"); - link_enter_failed(link); - return 1; - } - - if (link->ndisc_messages == 0) { - link->ndisc_configured = true; - r = link_request_set_routes(link); - if (r < 0) { - link_enter_failed(link); - return 1; - } - link_check_ready(link); - } - - return 1; -} - -static int ndisc_netlink_address_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->ndisc_messages > 0); - - link->ndisc_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_message_error_errno(link, m, r, "Could not set NDisc route or address"); - link_enter_failed(link); - return 1; - } else if (r >= 0) - (void) manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->ndisc_messages == 0) { - link->ndisc_configured = true; - r = link_request_set_routes(link); - if (r < 0) { - link_enter_failed(link); - return 1; - } - link_check_ready(link); - } - - return 1; -} - -static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { - _cleanup_(route_freep) Route *route = NULL; - union in_addr_union gateway; - uint16_t lifetime; - unsigned preference; - uint32_t mtu; - usec_t time_now; - int r; - Address *address; - Iterator i; - - assert(link); - assert(rt); - - r = sd_ndisc_router_get_lifetime(rt, &lifetime); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - - if (lifetime == 0) /* not a default router */ + if (!stableprivate_address_is_valid(addr)) { + *ret = NULL; return 0; - - r = sd_ndisc_router_get_address(rt, &gateway.in6); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - - SET_FOREACH(address, link->addresses, i) { - if (address->family != AF_INET6) - continue; - if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) { - _cleanup_free_ char *buffer = NULL; - - (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer); - log_link_debug(link, "No NDisc route added, gateway %s matches local address", - strnull(buffer)); - return 0; - } } - SET_FOREACH(address, link->addresses_foreign, i) { - if (address->family != AF_INET6) - continue; - if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) { - _cleanup_free_ char *buffer = NULL; - - (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer); - log_link_debug(link, "No NDisc route added, gateway %s matches local address", - strnull(buffer)); - return 0; - } - } - - r = sd_ndisc_router_get_preference(rt, &preference); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - - r = sd_ndisc_router_get_mtu(rt, &mtu); - if (r == -ENODATA) - mtu = 0; - else if (r < 0) - return log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m"); - - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - route->family = AF_INET6; - route->table = link_get_ipv6_accept_ra_route_table(link); - route->priority = link->network->dhcp_route_metric; - route->protocol = RTPROT_RA; - route->pref = preference; - route->gw = gateway; - route->lifetime = time_now + lifetime * USEC_PER_SEC; - route->mtu = mtu; - - r = route_configure(route, link, ndisc_netlink_route_message_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set default route: %m"); - link_enter_failed(link); - return r; - } - if (r > 0) - link->ndisc_messages++; - - Route *route_gw; - LIST_FOREACH(routes, route_gw, link->network->static_routes) { - if (!route_gw->gateway_from_dhcp) - continue; - - if (route_gw->family != AF_INET6) - continue; - - route_gw->gw = gateway; - - r = route_configure(route_gw, link, ndisc_netlink_route_message_handler); - if (r < 0) { - log_link_error_errno(link, r, "Could not set gateway: %m"); - link_enter_failed(link); - return r; - } - if (r > 0) - link->ndisc_messages++; - } - - return 0; + *ret = TAKE_PTR(addr); + return 1; } -static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) { +static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, uint8_t prefixlen, Set **ret) { _cleanup_set_free_free_ Set *addresses = NULL; - struct in6_addr addr; IPv6Token *j; Iterator i; int r; @@ -265,82 +376,63 @@ static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint3 assert(address); assert(ret); - addresses = set_new(&address_hash_ops); + addresses = set_new(&in6_addr_hash_ops); if (!addresses) return log_oom(); - addr = address->in_addr.in6; - ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i) { - bool have_address = false; - _cleanup_(address_freep) Address *new_address = NULL; - - r = address_new(&new_address); - if (r < 0) - return log_oom(); - - *new_address = *address; + ORDERED_SET_FOREACH(j, link->network->ipv6_tokens, i) { + _cleanup_free_ struct in6_addr *new_address = NULL; if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE - && memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) { + && IN6_ARE_ADDR_EQUAL(&j->prefix, address)) { /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop does not actually attempt Duplicate Address Detection; the counter will be incremented only when the address generation algorithm produces an invalid address, and the loop may exit with an address which ends up being unusable due to duplication on the link. */ for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) { - r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address->in_addr.in6); + r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address); if (r < 0) + return r; + if (r > 0) break; - - if (stableprivate_address_is_valid(&new_address->in_addr.in6)) { - have_address = true; - break; - } } } else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) { - memcpy(((uint8_t *)&new_address->in_addr.in6) + 8, ((uint8_t *) &j->prefix) + 8, 8); - have_address = true; + new_address = new(struct in6_addr, 1); + if (!new_address) + return log_oom(); + + memcpy(new_address->s6_addr, address->s6_addr, 8); + memcpy(new_address->s6_addr + 8, j->prefix.s6_addr + 8, 8); } - if (have_address) { - new_address->prefixlen = prefixlen; - new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; - new_address->cinfo.ifa_prefered = lifetime_preferred; - + if (new_address) { r = set_put(addresses, new_address); if (r < 0) - return log_link_warning_errno(link, r, "Failed to store address: %m"); - TAKE_PTR(new_address); + return log_link_error_errno(link, r, "Failed to store SLAAC address: %m"); + else if (r == 0) + log_link_debug_errno(link, r, "Generated SLAAC address is duplicated, ignoring."); + else + TAKE_PTR(new_address); } } /* fall back to EUI-64 if no tokens provided addresses */ if (set_isempty(addresses)) { - _cleanup_(address_freep) Address *new_address = NULL; + _cleanup_free_ struct in6_addr *new_address = NULL; - r = address_new(&new_address); - if (r < 0) + new_address = newdup(struct in6_addr, address, 1); + if (!new_address) return log_oom(); - *new_address = *address; - - /* see RFC4291 section 2.5.1 */ - new_address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0]; - new_address->in_addr.in6.s6_addr[8] ^= 1 << 1; - new_address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; - new_address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; - new_address->in_addr.in6.s6_addr[11] = 0xff; - new_address->in_addr.in6.s6_addr[12] = 0xfe; - new_address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; - new_address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; - new_address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5]; - new_address->prefixlen = prefixlen; - new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; - new_address->cinfo.ifa_prefered = lifetime_preferred; + r = generate_ipv6_eui_64_address(link, new_address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m"); r = set_put(addresses, new_address); if (r < 0) - return log_link_warning_errno(link, r, "Failed to store address: %m"); + return log_link_error_errno(link, r, "Failed to store SLAAC address: %m"); + TAKE_PTR(new_address); } @@ -353,9 +445,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining; _cleanup_set_free_free_ Set *addresses = NULL; _cleanup_(address_freep) Address *address = NULL; + struct in6_addr addr, *a; unsigned prefixlen; usec_t time_now; - Address *existing_address, *a; Iterator i; int r; @@ -364,7 +456,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) @@ -382,47 +474,49 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (lifetime_preferred > lifetime_valid) return 0; - r = address_new(&address); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate address: %m"); - - address->family = AF_INET6; - r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6); + r = sd_ndisc_router_prefix_get_address(rt, &addr); if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses); + r = ndisc_router_generate_addresses(link, &addr, prefixlen, &addresses); if (r < 0) - return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m"); + return r; + + r = address_new(&address); + if (r < 0) + return log_oom(); + + address->family = AF_INET6; + address->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; + address->cinfo.ifa_prefered = lifetime_preferred; SET_FOREACH(a, addresses, i) { + Address *existing_address; + /* see RFC4862 section 5.5.3.e */ - r = address_get(link, a->family, &a->in_addr, a->prefixlen, &existing_address); + r = address_get(link, AF_INET6, (union in_addr_union *) a, prefixlen, &existing_address); if (r > 0) { lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC; if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining) - a->cinfo.ifa_valid = lifetime_valid; + address->cinfo.ifa_valid = lifetime_valid; else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN) - a->cinfo.ifa_valid = lifetime_remaining; + address->cinfo.ifa_valid = lifetime_remaining; else - a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN; + address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN; } else if (lifetime_valid > 0) - a->cinfo.ifa_valid = lifetime_valid; + address->cinfo.ifa_valid = lifetime_valid; else - return 0; /* see RFC4862 section 5.5.3.d */ + continue; /* see RFC4862 section 5.5.3.d */ - if (a->cinfo.ifa_valid == 0) + if (address->cinfo.ifa_valid == 0) continue; - r = address_configure(a, link, ndisc_netlink_address_message_handler, true); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); - link_enter_failed(link); - return r; - } + address->in_addr.in6 = *a; - if (r > 0) - link->ndisc_messages++; + r = ndisc_address_configure(address, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set SLAAC address: %m"); } return 0; @@ -440,7 +534,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) @@ -452,11 +546,11 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { r = route_new(&route); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); + return log_oom(); route->family = AF_INET6; route->table = link_get_ipv6_accept_ra_route_table(link); - route->priority = link->network->dhcp_route_metric; + route->priority = link->network->dhcp6_route_metric; route->protocol = RTPROT_RA; route->flags = RTM_F_PREFIX; route->dst_prefixlen = prefixlen; @@ -466,14 +560,9 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - r = route_configure(route, link, ndisc_netlink_route_message_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set prefix route: %m"); - link_enter_failed(link); - return r; - } - if (r > 0) - link->ndisc_messages++; + r = ndisc_route_configure(route, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set prefix route: %m");; return 0; } @@ -490,33 +579,34 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m"); if (lifetime == 0) return 0; r = sd_ndisc_router_get_address(rt, &gateway); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get route prefix length: %m"); + return log_link_error_errno(link, r, "Failed to get route prefix length: %m"); r = sd_ndisc_router_route_get_preference(rt, &preference); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); + 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(), &time_now); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); r = route_new(&route); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); + return log_oom(); route->family = AF_INET6; route->table = link_get_ipv6_accept_ra_route_table(link); + route->priority = link->network->dhcp6_route_metric; route->protocol = RTPROT_RA; route->pref = preference; route->gw.in6 = gateway; @@ -527,14 +617,9 @@ 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 route address: %m"); - r = route_configure(route, link, ndisc_netlink_route_message_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set additional route: %m"); - link_enter_failed(link); - return r; - } - if (r > 0) - link->ndisc_messages++; + r = ndisc_route_configure(route, link); + if (r < 0) + return log_link_error_errno(link, r, "Could not set additional route: %m"); return 0; } @@ -547,188 +632,172 @@ static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) { return memcmp(&a->address, &b->address, sizeof(a->address)); } -DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_rdnss_hash_ops, + NDiscRDNSS, + ndisc_rdnss_hash_func, + ndisc_rdnss_compare_func, + free); static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { uint32_t lifetime; const struct in6_addr *a; + NDiscRDNSS *rdnss; usec_t time_now; - int i, n, r; + Iterator i; + int n, r; assert(link); assert(rt); r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); + return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m"); n = sd_ndisc_router_rdnss_get_addresses(rt, &a); if (n < 0) - return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m"); + return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); - for (i = 0; i < n; i++) { + SET_FOREACH(rdnss, link->ndisc_rdnss, i) + rdnss->marked = true; + + if (lifetime == 0) + return 0; + + if (n >= (int) NDISC_RDNSS_MAX) { + log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX); + n = NDISC_RDNSS_MAX; + } + + for (int j = 0; j < n; j++) { _cleanup_free_ NDiscRDNSS *x = NULL; NDiscRDNSS d = { - .address = a[i], - }, *y; + .address = a[j], + }; - if (lifetime == 0) { - (void) set_remove(link->ndisc_rdnss, &d); - link_dirty(link); + rdnss = set_get(link->ndisc_rdnss, &d); + if (rdnss) { + rdnss->marked = false; + rdnss->valid_until = time_now + lifetime * USEC_PER_SEC; continue; } - y = set_get(link->ndisc_rdnss, &d); - if (y) { - y->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { - log_link_warning(link, "Too many RDNSS records per link, ignoring."); - continue; - } - - r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops); - if (r < 0) - return log_oom(); - x = new(NDiscRDNSS, 1); if (!x) return log_oom(); *x = (NDiscRDNSS) { - .address = a[i], + .address = a[j], .valid_until = time_now + lifetime * USEC_PER_SEC, }; - r = set_put(link->ndisc_rdnss, x); + r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x)); if (r < 0) return log_oom(); - - TAKE_PTR(x); - assert(r > 0); - link_dirty(link); } return 0; } static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) { - siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state); + siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state); } static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) { return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); } -DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + ndisc_dnssl_hash_ops, + NDiscDNSSL, + ndisc_dnssl_hash_func, + ndisc_dnssl_compare_func, + free); -static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { +static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { _cleanup_strv_free_ char **l = NULL; uint32_t lifetime; usec_t time_now; - char **i; + NDiscDNSSL *dnssl; + Iterator i; + char **j; int r; assert(link); assert(rt); r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } + if (r < 0) + return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); - return; - } + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m"); r = sd_ndisc_router_dnssl_get_domains(rt, &l); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m"); - return; + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); + + SET_FOREACH(dnssl, link->ndisc_dnssl, i) + dnssl->marked = true; + + if (lifetime == 0) + return 0; + + if (strv_length(l) >= NDISC_DNSSL_MAX) { + log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX); + STRV_FOREACH(j, l + NDISC_DNSSL_MAX) + *j = mfree(*j); } - STRV_FOREACH(i, l) { - _cleanup_free_ NDiscDNSSL *s; - NDiscDNSSL *x; + STRV_FOREACH(j, l) { + _cleanup_free_ NDiscDNSSL *s = NULL; - s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1); - if (!s) { - log_oom(); - return; - } + s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); + if (!s) + return log_oom(); - strcpy(NDISC_DNSSL_DOMAIN(s), *i); + strcpy(NDISC_DNSSL_DOMAIN(s), *j); - if (lifetime == 0) { - (void) set_remove(link->ndisc_dnssl, s); - link_dirty(link); + dnssl = set_get(link->ndisc_dnssl, s); + if (dnssl) { + dnssl->marked = false; + dnssl->valid_until = time_now + lifetime * USEC_PER_SEC; continue; } - x = set_get(link->ndisc_dnssl, s); - if (x) { - x->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { - log_link_warning(link, "Too many DNSSL records per link, ignoring."); - continue; - } - - r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops); - if (r < 0) { - log_oom(); - return; - } - s->valid_until = time_now + lifetime * USEC_PER_SEC; - r = set_put(link->ndisc_dnssl, s); - if (r < 0) { - log_oom(); - return; - } - - s = NULL; + r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s)); + if (r < 0) + return log_oom(); assert(r > 0); - link_dirty(link); } + + return 0; } static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { - int r; - assert(link); assert(link->network); assert(rt); - r = sd_ndisc_router_option_rewind(rt); - for (;;) { + for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) { uint8_t type; if (r < 0) - return log_link_warning_errno(link, r, "Failed to iterate through options: %m"); + return log_link_error_errno(link, r, "Failed to iterate through options: %m"); if (r == 0) /* EOF */ - break; + return 0; r = sd_ndisc_router_option_get_type(rt, &type); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA option type: %m"); + return log_link_error_errno(link, r, "Failed to get RA option type: %m"); switch (type) { @@ -740,54 +809,64 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get prefix address: %m"); - if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) { + if (set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) { if (DEBUG_LOGGING) { _cleanup_free_ char *b = NULL; (void) in_addr_to_string(AF_INET6, &a, &b); - log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b)); + log_link_debug(link, "Prefix '%s' is deny-listed, ignoring", strna(b)); } - break; } r = sd_ndisc_router_prefix_get_flags(rt, &flags); if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); + return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m"); if (link->network->ipv6_accept_ra_use_onlink_prefix && - FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) - (void) ndisc_router_process_onlink_prefix(link, rt); + FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) { + r = ndisc_router_process_onlink_prefix(link, rt); + if (r < 0) + return r; + } if (link->network->ipv6_accept_ra_use_autonomous_prefix && - FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) - (void) ndisc_router_process_autonomous_prefix(link, rt); - + FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) { + r = ndisc_router_process_autonomous_prefix(link, rt); + if (r < 0) + return r; + } break; } case SD_NDISC_OPTION_ROUTE_INFORMATION: - (void) ndisc_router_process_route(link, rt); + r = ndisc_router_process_route(link, rt); + if (r < 0) + return r; break; case SD_NDISC_OPTION_RDNSS: - if (link->network->ipv6_accept_ra_use_dns) - (void) ndisc_router_process_rdnss(link, rt); + if (link->network->ipv6_accept_ra_use_dns) { + r = ndisc_router_process_rdnss(link, rt); + if (r < 0) + return r; + } break; case SD_NDISC_OPTION_DNSSL: - if (link->network->ipv6_accept_ra_use_dns) - (void) ndisc_router_process_dnssl(link, rt); + if (link->network->ipv6_accept_ra_use_dns) { + r = ndisc_router_process_dnssl(link, rt); + if (r < 0) + return r; + } break; } - - r = sd_ndisc_router_option_next(rt); } - - return 0; } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { + Address *address; + Route *route; uint64_t flags; int r; @@ -796,29 +875,79 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { assert(link->manager); assert(rt); - r = sd_ndisc_router_get_flags(rt, &flags); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get RA flags: %m"); + link->ndisc_addresses_configured = false; + link->ndisc_routes_configured = false; - if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { - /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ - r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); - if (r < 0 && r != -EBUSY) - log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); - else { - log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); - r = 0; - } + link_dirty(link); + + while ((address = set_steal_first(link->ndisc_addresses))) { + r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m"); } - (void) ndisc_router_process_default(link, rt); - (void) ndisc_router_process_options(link, rt); + while ((route = set_steal_first(link->ndisc_routes))) { + r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route); + if (r < 0) + return log_link_error_errno(link, r, "Failed to store old NDisc route: %m"); + } - return r; + r = sd_ndisc_router_get_flags(rt, &flags); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get RA flags: %m"); + + if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) && link->network->ipv6_accept_ra_start_dhcp6_client)) { + + if (link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS) + r = dhcp6_request_address(link, false); + else + /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ + r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); + if (r < 0 && r != -EBUSY) + return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); + else + log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); + } + + r = ndisc_router_process_default(link, rt); + if (r < 0) + return r; + r = ndisc_router_process_options(link, rt); + if (r < 0) + return r; + + if (link->ndisc_addresses_messages == 0) + link->ndisc_addresses_configured = true; + else { + log_link_debug(link, "Setting SLAAC addresses."); + + /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). + * Before they are called, the related flags must be cleared. Otherwise, the link + * becomes configured state before routes are configured. */ + link->static_routes_configured = false; + link->static_nexthops_configured = false; + } + + if (link->ndisc_routes_messages == 0) + link->ndisc_routes_configured = true; + else + log_link_debug(link, "Setting NDisc routes."); + + r = ndisc_remove_old(link, false); + if (r < 0) + return r; + + if (link->ndisc_addresses_configured && link->ndisc_routes_configured) + link_check_ready(link); + else + link_set_state(link, LINK_STATE_CONFIGURING); + + return 0; } static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { Link *link = userdata; + int r; assert(link); @@ -828,16 +957,23 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r switch (event) { case SD_NDISC_EVENT_ROUTER: - (void) ndisc_router_handler(link, rt); + r = ndisc_router_handler(link, rt); + if (r < 0) { + link_enter_failed(link); + return; + } break; case SD_NDISC_EVENT_TIMEOUT: - link->ndisc_configured = true; - link_check_ready(link); - + log_link_debug(link, "NDisc handler get timeout event"); + if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) { + link->ndisc_addresses_configured = true; + link->ndisc_routes_configured = true; + link_check_ready(link); + } break; default: - log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); + assert_not_reached("Unknown NDisc event"); } } @@ -874,6 +1010,7 @@ void ndisc_vacuum(Link *link) { NDiscDNSSL *d; Iterator i; usec_t time_now; + bool updated = false; assert(link); @@ -884,14 +1021,17 @@ void ndisc_vacuum(Link *link) { SET_FOREACH(r, link->ndisc_rdnss, i) if (r->valid_until < time_now) { free(set_remove(link->ndisc_rdnss, r)); - link_dirty(link); + updated = true; } SET_FOREACH(d, link->ndisc_dnssl, i) if (d->valid_until < time_now) { free(set_remove(link->ndisc_dnssl, d)); - link_dirty(link); + updated = true; } + + if (updated) + link_dirty(link); } void ndisc_flush(Link *link) { @@ -899,8 +1039,8 @@ void ndisc_flush(Link *link) { /* Removes all RDNSS and DNSSL entries, without exception */ - link->ndisc_rdnss = set_free_free(link->ndisc_rdnss); - link->ndisc_dnssl = set_free_free(link->ndisc_dnssl); + link->ndisc_rdnss = set_free(link->ndisc_rdnss); + link->ndisc_dnssl = set_free(link->ndisc_dnssl); } int ipv6token_new(IPv6Token **ret) { @@ -919,15 +1059,29 @@ int ipv6token_new(IPv6Token **ret) { return 0; } -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( +static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) { + siphash24_compress(&p->address_generation_type, sizeof(p->address_generation_type), state); + siphash24_compress(&p->prefix, sizeof(p->prefix), state); +} + +static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) { + int r; + + r = CMP(a->address_generation_type, b->address_generation_type); + if (r != 0) + return r; + + return memcmp(&a->prefix, &b->prefix, sizeof(struct in6_addr)); +} + +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( ipv6_token_hash_ops, - void, - trivial_hash_func, - trivial_compare_func, IPv6Token, + ipv6_token_hash_func, + ipv6_token_compare_func, free); -int config_parse_ndisc_black_listed_prefix( +int config_parse_ndisc_deny_listed_prefix( const char *unit, const char *filename, unsigned line, @@ -949,7 +1103,7 @@ int config_parse_ndisc_black_listed_prefix( assert(data); if (isempty(rvalue)) { - network->ndisc_black_listed_prefix = set_free_free(network->ndisc_black_listed_prefix); + network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix); return 0; } @@ -959,9 +1113,11 @@ int config_parse_ndisc_black_listed_prefix( union in_addr_union ip; r = extract_first_word(&p, &n, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse NDISC black listed prefix, ignoring assignment: %s", + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse NDisc deny-listed prefix, ignoring assignment: %s", rvalue); return 0; } @@ -970,33 +1126,22 @@ int config_parse_ndisc_black_listed_prefix( r = in_addr_from_string(AF_INET6, n, &ip); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "NDISC black listed prefix is invalid, ignoring assignment: %s", n); + log_syntax(unit, LOG_WARNING, filename, line, r, + "NDisc deny-listed prefix is invalid, ignoring assignment: %s", n); continue; } - if (set_contains(network->ndisc_black_listed_prefix, &ip.in6)) + if (set_contains(network->ndisc_deny_listed_prefix, &ip.in6)) continue; - r = set_ensure_allocated(&network->ndisc_black_listed_prefix, &in6_addr_hash_ops); - if (r < 0) - return log_oom(); - a = newdup(struct in6_addr, &ip.in6, 1); if (!a) return log_oom(); - r = set_put(network->ndisc_black_listed_prefix, a); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n); - continue; - } - - TAKE_PTR(a); + r = set_ensure_consume(&network->ndisc_deny_listed_prefix, &in6_addr_hash_ops, TAKE_PTR(a)); + if (r < 0) + return log_oom(); } - - return 0; } int config_parse_address_generation_type( @@ -1023,7 +1168,7 @@ int config_parse_address_generation_type( assert(data); if (isempty(rvalue)) { - network->ipv6_tokens = ordered_hashmap_free(network->ipv6_tokens); + network->ipv6_tokens = ordered_set_free(network->ipv6_tokens); return 0; } @@ -1042,31 +1187,42 @@ int config_parse_address_generation_type( r = in_addr_from_string(AF_INET6, p, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue); return 0; } if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue); return 0; } token->prefix = buffer.in6; - r = ordered_hashmap_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops); + r = ordered_set_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops); if (r < 0) return log_oom(); - r = ordered_hashmap_put(network->ipv6_tokens, &token->prefix, token); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to store IPv6 token '%s'", rvalue); - return 0; - } - - TAKE_PTR(token); + r = ordered_set_put(network->ipv6_tokens, token); + if (r == -EEXIST) + log_syntax(unit, LOG_DEBUG, filename, line, r, + "IPv6 token '%s' is duplicated, ignoring: %m", rvalue); + else if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to store IPv6 token '%s', ignoring: %m", rvalue); + else + TAKE_PTR(token); return 0; } + +DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, + "Failed to parse DHCPv6Client= setting") +static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = { + [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no", + [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always", + [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES); diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 02c2f8afd..c459f4245 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -15,12 +15,24 @@ typedef enum IPv6TokenAddressGeneration { _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -1, } IPv6TokenAddressGeneration; +typedef enum IPv6AcceptRAStartDHCP6Client { + IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO, + IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS, + IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES, + _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX, + _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -1, +} IPv6AcceptRAStartDHCP6Client; + typedef struct NDiscRDNSS { + /* Used when GC'ing old DNS servers when configuration changes. */ + bool marked; usec_t valid_until; struct in6_addr address; } NDiscRDNSS; typedef struct NDiscDNSSL { + /* Used when GC'ing old domains when configuration changes. */ + bool marked; usec_t valid_until; /* The domain name follows immediately. */ } NDiscDNSSL; @@ -43,5 +55,9 @@ int ndisc_configure(Link *link); void ndisc_vacuum(Link *link); void ndisc_flush(Link *link); -CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix); +CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_deny_listed_prefix); CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type); +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client); + +const char* ipv6_accept_ra_start_dhcp6_client_to_string(IPv6AcceptRAStartDHCP6Client i) _const_; +IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client_from_string(const char *s) _pure_; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index fd6219fcc..09ddb9c8a 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -132,7 +132,7 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand if (r < 0) return log_link_error_errno(link, r, "Could not set state: %m"); - r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE); + r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE); if (r < 0) return log_link_error_errno(link, r, "Could not set flags: %m"); @@ -247,7 +247,7 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size); } -DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free); int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) { Neighbor neighbor, *existing; @@ -300,11 +300,7 @@ static int neighbor_add_internal(Link *link, Set **neighbors, int family, const .lladdr_size = lladdr_size, }; - r = set_ensure_allocated(neighbors, &neighbor_hash_ops); - if (r < 0) - return r; - - r = set_put(*neighbors, neighbor); + r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor); if (r < 0) return r; if (r == 0) @@ -314,8 +310,7 @@ static int neighbor_add_internal(Link *link, Set **neighbors, int family, const if (ret) *ret = neighbor; - - neighbor = NULL; + TAKE_PTR(neighbor); return 0; } @@ -332,11 +327,7 @@ int neighbor_add(Link *link, int family, const union in_addr_union *addr, const return r; } else if (r == 0) { /* Neighbor is foreign, claim it as recognized */ - r = set_ensure_allocated(&link->neighbors, &neighbor_hash_ops); - if (r < 0) - return r; - - r = set_put(link->neighbors, neighbor); + r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor); if (r < 0) return r; @@ -408,11 +399,12 @@ int config_parse_neighbor_address( r = neighbor_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Neighbor Address is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -445,7 +437,7 @@ int config_parse_neighbor_lladdr( r = neighbor_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = ether_addr_from_string(rvalue, &n->lladdr.mac); if (r >= 0) @@ -453,7 +445,7 @@ int config_parse_neighbor_lladdr( else { r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s", rvalue); return 0; @@ -490,11 +482,11 @@ int config_parse_neighbor_hwaddr( r = neighbor_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = ether_addr_from_string(rvalue, &n->lladdr.mac); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue); return 0; } diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 920ac41b4..3f1652b19 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -6,14 +6,18 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "conf-parser.h" #include "netem.h" #include "network-internal.h" +#include "networkd-can.h" #include "networkd-conf.h" #include "networkd-dhcp-common.h" #include "networkd-dhcp-server.h" #include "networkd-dhcp4.h" +#include "networkd-dhcp6.h" #include "networkd-ipv4ll.h" #include "networkd-ndisc.h" #include "networkd-network.h" +#include "networkd-sriov.h" #include "qdisc.h" +#include "tclass.h" #include "vlan-util.h" %} struct ConfigPerfItem; @@ -44,11 +48,21 @@ Match.KernelVersion, config_parse_net_condition, Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu) +Link.Group, config_parse_uint32, 0, offsetof(Network, group) Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast) Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast) Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged) Link.RequiredForOnline, config_parse_required_for_online, 0, 0 +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 Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name) Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name) @@ -66,6 +80,7 @@ Network.VRF, config_parse_ifname, Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) Network.LinkLocalAddressing, config_parse_link_local_address_family, 0, offsetof(Network, link_local) +Network.IPv6LinkLocalAddressGenerationMode, config_parse_ipv6_link_local_address_gen_mode, 0, offsetof(Network, ipv6ll_address_gen_mode) Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) Network.DefaultRouteOnDevice, config_parse_bool, 0, offsetof(Network, default_route_on_device) Network.IPv6Token, config_parse_address_generation_type, 0, 0 @@ -91,6 +106,7 @@ Network.IPv6DuplicateAddressDetection, config_parse_int, Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu) +Network.IPv4AcceptLocal, config_parse_tristate, 0, offsetof(Network, ipv4_accept_local) Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave) Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) @@ -98,7 +114,7 @@ Network.ProxyARP, config_parse_tristate, Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier) -Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss) +Network.IgnoreCarrierLoss, config_parse_tristate, 0, offsetof(Network, ignore_carrier_loss) Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 @@ -140,73 +156,95 @@ Route.Scope, config_parse_route_scope, Route.PreferredSource, config_parse_preferred_src, 0, 0 Route.Table, config_parse_route_table, 0, 0 Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0 -Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0 -Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 +Route.GatewayOnLink, config_parse_route_boolean, 0, 0 +Route.GatewayOnlink, config_parse_route_boolean, 0, 0 Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 Route.Protocol, config_parse_route_protocol, 0, 0 Route.Type, config_parse_route_type, 0, 0 Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0 Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0 -Route.QuickAck, config_parse_quickack, 0, 0 -Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0 -Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0 +Route.QuickAck, config_parse_route_boolean, 0, 0 +Route.FastOpenNoCookie, config_parse_route_boolean, 0, 0 +Route.TTLPropagate, config_parse_route_boolean, 0, 0 Route.MultiPathRoute, config_parse_multipath_route, 0, 0 NextHop.Id, config_parse_nexthop_id, 0, 0 NextHop.Gateway, config_parse_nexthop_gateway, 0, 0 DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) -DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) +DHCPv4.UseDNS, config_parse_dhcp_use_dns, 0, 0 DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns) -DHCPv4.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) +DHCPv4.UseNTP, config_parse_dhcp_use_ntp, 0, 0 DHCPv4.UseSIP, config_parse_bool, 0, offsetof(Network, dhcp_use_sip) DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) DHCPv4.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes) -DHCPv4.RequestOptions, config_parse_dhcp_request_options, 0, 0 +DHCPv4.UseGateway, config_parse_tristate, 0, offsetof(Network, dhcp_use_gateway) +DHCPv4.RequestOptions, config_parse_dhcp_request_options, AF_INET, 0 DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize) DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname) DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) DHCPv4.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCPv4.MUDURL, config_parse_dhcp_mud_url, 0, 0 DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0 -DHCPv4.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) +DHCPv4.UserClass, config_parse_dhcp_user_class, AF_INET, offsetof(Network, dhcp_user_class) DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) -DHCPv4.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_route_metric) +DHCPv4.RouteMetric, config_parse_dhcp_route_metric, 0, 0 DHCPv4.RouteTable, config_parse_section_route_table, 0, 0 DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCPv4.IAID, config_parse_iaid, 0, 0 DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release) DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline) -DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 +DHCPv4.DenyList, config_parse_dhcp_acl_ip_address, 0, 0 +DHCPv4.AllowList, config_parse_dhcp_acl_ip_address, 0, 0 DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, ip_service_type) -DHCPv4.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_options) +DHCPv4.SendOption, config_parse_dhcp_send_option, AF_INET, offsetof(Network, dhcp_client_send_options) +DHCPv4.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_vendor_options) DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu) -DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns) -DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp) +DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0 +DHCPv6.UseDNS, config_parse_dhcp_use_dns, 0, 0 +DHCPv6.UseNTP, config_parse_dhcp_use_ntp, 0, 0 DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) +DHCPv6.MUDURL, config_parse_dhcp6_mud_url, 0, 0 +DHCPv6.RequestOptions, config_parse_dhcp_request_options, AF_INET6, 0 +DHCPv6.UserClass, config_parse_dhcp_user_class, AF_INET6, offsetof(Network, dhcp6_user_class) +DHCPv6.VendorClass, config_parse_dhcp_vendor_class, 0, offsetof(Network, dhcp6_vendor_class) +DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options) DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0 +DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra) +DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options) +DHCPv6.RouteMetric, config_parse_dhcp_route_metric, 0, 0 IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) +IPv6AcceptRA.DHCPv6Client, config_parse_ipv6_accept_ra_start_dhcp6_client, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client) IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0 -IPv6AcceptRA.BlackList, config_parse_ndisc_black_listed_prefix, 0, 0 +IPv6AcceptRA.DenyList, config_parse_ndisc_deny_listed_prefix, 0, 0 +IPv6AcceptRA.BlackList, config_parse_ndisc_deny_listed_prefix, 0, 0 DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) -DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) -DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 -DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) -DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 -DHCPServer.EmitSIP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_sip) -DHCPServer.SIP, config_parse_dhcp_server_sip, 0, 0 +DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS].emit) +DHCPServer.DNS, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS]) +DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_NTP].emit) +DHCPServer.NTP, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_NTP]) +DHCPServer.EmitSIP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SIP].emit) +DHCPServer.SIP, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SIP]) +DHCPServer.EmitPOP3, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_POP3].emit) +DHCPServer.POP3, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_POP3]) +DHCPServer.EmitSMTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SMTP].emit) +DHCPServer.SMTP, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SMTP]) +DHCPServer.EmitLPR, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_LPR].emit) +DHCPServer.LPR, config_parse_dhcp_server_emit, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_LPR]) DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) +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) Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) @@ -230,7 +268,10 @@ BridgeFDB.AssociatedWith, config_parse_fdb_ntf_flags, BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 -Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0 +Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, offsetof(Network, router_prefix_delegation) +DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp6_pd_subnet_id, 0, offsetof(Network, dhcp6_pd_subnet_id) +DHCPv6PrefixDelegation.Assign, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign) +DHCPv6PrefixDelegation.Token, config_parse_dhcp6_pd_token, 0, offsetof(Network, dhcp6_pd_token) IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) @@ -245,14 +286,29 @@ IPv6Prefix.OnLink, config_parse_prefix_flags, IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 +IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0 IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0 IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0 -CAN.BitRate, config_parse_si_uint64, 0, offsetof(Network, can_bitrate) +LLDP.MUDURL, config_parse_lldp_mud, 0, 0 +CAN.BitRate, config_parse_can_bitrate, 0, offsetof(Network, can_bitrate) CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point) +CAN.DataBitRate, config_parse_can_bitrate, 0, offsetof(Network, can_data_bitrate) +CAN.DataSamplePoint, config_parse_permille, 0, offsetof(Network, can_data_sample_point) +CAN.FDMode, config_parse_tristate, 0, offsetof(Network, can_fd_mode) +CAN.FDNonISO, config_parse_tristate, 0, offsetof(Network, can_non_iso) CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us) CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling) +CAN.Termination, config_parse_tristate, 0, offsetof(Network, can_termination) +CAN.ListenOnly, config_parse_tristate, 0, offsetof(Network, can_listen_only) QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0 QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0 +BFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_BFIFO, 0 +BFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_BFIFO, 0 +BFIFO.LimitBytes, config_parse_bfifo_size, QDISC_KIND_BFIFO, 0 +CAKE.Parent, config_parse_qdisc_parent, QDISC_KIND_CAKE, 0 +CAKE.Handle, config_parse_qdisc_handle, QDISC_KIND_CAKE, 0 +CAKE.Bandwidth, config_parse_cake_bandwidth, QDISC_KIND_CAKE, 0 +CAKE.OverheadBytes, config_parse_cake_overhead, QDISC_KIND_CAKE, 0 ControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_CODEL, 0 ControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_CODEL, 0 ControlledDelay.PacketLimit, config_parse_controlled_delay_u32, QDISC_KIND_CODEL, 0 @@ -260,12 +316,37 @@ ControlledDelay.TargetSec, config_parse_controlled_delay_usec, ControlledDelay.IntervalSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0 ControlledDelay.CEThresholdSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0 ControlledDelay.ECN, config_parse_controlled_delay_bool, QDISC_KIND_CODEL, 0 +DeficitRoundRobinScheduler.Parent, config_parse_qdisc_parent, QDISC_KIND_DRR, 0 +DeficitRoundRobinScheduler.Handle, config_parse_qdisc_handle, QDISC_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.Parent, config_parse_tclass_parent, TCLASS_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_DRR, 0 +DeficitRoundRobinSchedulerClass.QuantumBytes, config_parse_drr_size, TCLASS_KIND_DRR, 0 +EnhancedTransmissionSelection.Parent, config_parse_qdisc_parent, QDISC_KIND_ETS, 0 +EnhancedTransmissionSelection.Handle, config_parse_qdisc_handle, QDISC_KIND_ETS, 0 +EnhancedTransmissionSelection.Bands, config_parse_ets_u8, QDISC_KIND_ETS, 0 +EnhancedTransmissionSelection.StrictBands, config_parse_ets_u8, QDISC_KIND_ETS, 0 +EnhancedTransmissionSelection.QuantumBytes, config_parse_ets_quanta, QDISC_KIND_ETS, 0 +EnhancedTransmissionSelection.PriorityMap, config_parse_ets_prio, QDISC_KIND_ETS, 0 +PFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO, 0 +PFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO, 0 +PFIFO.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO, 0 +PFIFOFast.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO_FAST, 0 +PFIFOFast.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO_FAST, 0 +PFIFOHeadDrop.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO_HEAD_DROP, 0 +PFIFOHeadDrop.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO_HEAD_DROP, 0 +PFIFOHeadDrop.PacketLimit, config_parse_pfifo_size, QDISC_KIND_PFIFO_HEAD_DROP, 0 +QuickFairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_QFQ, 0 +QuickFairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_QFQ, 0 +QuickFairQueueingClass.Parent, config_parse_tclass_parent, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.Weight, config_parse_quick_fair_queueing_weight, TCLASS_KIND_QFQ, 0 +QuickFairQueueingClass.MaxPacketBytes, config_parse_quick_fair_queueing_max_packet, TCLASS_KIND_QFQ, 0 FairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ, 0 FairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ, 0 FairQueueing.PacketLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0 FairQueueing.FlowLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0 -FairQueueing.Quantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 -FairQueueing.InitialQuantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 +FairQueueing.QuantumBytes, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 +FairQueueing.InitialQuantumBytes, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 FairQueueing.MaximumRate, config_parse_fair_queueing_max_rate, QDISC_KIND_FQ, 0 FairQueueing.Buckets, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0 FairQueueing.OrphanMask, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0 @@ -274,13 +355,35 @@ FairQueueing.CEThresholdSec, config_parse_fair_queueing_usec, FairQueueingControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.PacketLimit, config_parse_fair_queueing_controlled_delay_u32, QDISC_KIND_FQ_CODEL, 0 -FairQueueingControlledDelay.MemoryLimit, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 +FairQueueingControlledDelay.MemoryLimitBytes, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.Flows, config_parse_fair_queueing_controlled_delay_u32, QDISC_KIND_FQ_CODEL, 0 -FairQueueingControlledDelay.Quantum, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 +FairQueueingControlledDelay.QuantumBytes, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.TargetSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.IntervalSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.CEThresholdSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0 FairQueueingControlledDelay.ECN, config_parse_fair_queueing_controlled_delay_bool, QDISC_KIND_FQ_CODEL, 0 +GenericRandomEarlyDetection.Parent, config_parse_qdisc_parent, QDISC_KIND_GRED, 0 +GenericRandomEarlyDetection.Handle, config_parse_qdisc_handle, QDISC_KIND_GRED, 0 +GenericRandomEarlyDetection.VirtualQueues, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0 +GenericRandomEarlyDetection.DefaultVirtualQueue, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0 +GenericRandomEarlyDetection.GenericRIO, config_parse_generic_random_early_detection_bool, QDISC_KIND_GRED, 0 +HeavyHitterFilter.Parent, config_parse_qdisc_parent, QDISC_KIND_HHF, 0 +HeavyHitterFilter.Handle, config_parse_qdisc_handle, QDISC_KIND_HHF, 0 +HeavyHitterFilter.PacketLimit, config_parse_heavy_hitter_filter_packet_limit, QDISC_KIND_HHF, 0 +HierarchyTokenBucket.Parent, config_parse_qdisc_parent, QDISC_KIND_HTB, 0 +HierarchyTokenBucket.Handle, config_parse_qdisc_handle, QDISC_KIND_HTB, 0 +HierarchyTokenBucket.DefaultClass, config_parse_hierarchy_token_bucket_default_class, QDISC_KIND_HTB, 0 +HierarchyTokenBucket.RateToQuantum, config_parse_hierarchy_token_bucket_u32, QDISC_KIND_HTB, 0 +HierarchyTokenBucketClass.Parent, config_parse_tclass_parent, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.Priority, config_parse_hierarchy_token_bucket_class_u32, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.QuantumBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.MTUBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.OverheadBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.Rate, config_parse_hierarchy_token_bucket_class_rate, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.CeilRate, config_parse_hierarchy_token_bucket_class_rate, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.BufferBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 +HierarchyTokenBucketClass.CeilBufferBytes, config_parse_hierarchy_token_bucket_class_size, TCLASS_KIND_HTB, 0 NetworkEmulator.Parent, config_parse_qdisc_parent, QDISC_KIND_NETEM, 0 NetworkEmulator.Handle, config_parse_qdisc_handle, QDISC_KIND_NETEM, 0 NetworkEmulator.DelaySec, config_parse_network_emulator_delay, QDISC_KIND_NETEM, 0 @@ -288,23 +391,30 @@ NetworkEmulator.DelayJitterSec, config_parse_network_emulator_delay NetworkEmulator.LossRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0 NetworkEmulator.DuplicateRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0 NetworkEmulator.PacketLimit, config_parse_network_emulator_packet_limit, QDISC_KIND_NETEM, 0 +PIE.Parent, config_parse_qdisc_parent, QDISC_KIND_PIE, 0 +PIE.Handle, config_parse_qdisc_handle, QDISC_KIND_PIE, 0 +PIE.PacketLimit, config_parse_pie_packet_limit, QDISC_KIND_PIE, 0 +StochasticFairBlue.Parent, config_parse_qdisc_parent, QDISC_KIND_SFB, 0 +StochasticFairBlue.Handle, config_parse_qdisc_handle, QDISC_KIND_SFB, 0 +StochasticFairBlue.PacketLimit, config_parse_stochastic_fair_blue_u32, QDISC_KIND_SFB, 0 StochasticFairnessQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_SFQ, 0 StochasticFairnessQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_SFQ, 0 StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ, 0 TokenBucketFilter.Parent, config_parse_qdisc_parent, QDISC_KIND_TBF, 0 TokenBucketFilter.Handle, config_parse_qdisc_handle, QDISC_KIND_TBF, 0 -TokenBucketFilter.Rate, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 -TokenBucketFilter.Burst, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 -TokenBucketFilter.LimitSize, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 +TokenBucketFilter.Rate, config_parse_token_bucket_filter_rate, QDISC_KIND_TBF, 0 +TokenBucketFilter.BurstBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 +TokenBucketFilter.LimitBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 TokenBucketFilter.MTUBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 TokenBucketFilter.MPUBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 -TokenBucketFilter.PeakRate, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 +TokenBucketFilter.PeakRate, config_parse_token_bucket_filter_rate, QDISC_KIND_TBF, 0 TokenBucketFilter.LatencySec, config_parse_token_bucket_filter_latency, QDISC_KIND_TBF, 0 TrivialLinkEqualizer.Parent, config_parse_qdisc_parent, QDISC_KIND_TEQL, 0 TrivialLinkEqualizer.Handle, config_parse_qdisc_handle, QDISC_KIND_TEQL, 0 TrivialLinkEqualizer.Id, config_parse_trivial_link_equalizer_id, QDISC_KIND_TEQL, 0 /* backwards compatibility: do not add new entries to this section */ Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) +DHCPv4.BlackList, config_parse_dhcp_acl_ip_address, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_dhcp_use_dns, 0, 0 DHCP.UseNTP, config_parse_dhcp_use_ntp, 0, 0 @@ -319,10 +429,10 @@ DHCP.Hostname, config_parse_hostname, DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) -DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) +DHCP.UserClass, config_parse_dhcp_user_class, AF_INET, offsetof(Network, dhcp_user_class) DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) -DHCP.RouteMetric, config_parse_uint32, 0, offsetof(Network, dhcp_route_metric) +DHCP.RouteMetric, config_parse_dhcp_route_metric, 0, 0 DHCP.RouteTable, config_parse_section_route_table, 0, 0 DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, 0 @@ -337,3 +447,9 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_net TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_network_emulator_rate, 0, 0 TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_network_emulator_rate, 0, 0 TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_network_emulator_packet_limit, 0, 0 +FairQueueing.Quantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 +FairQueueing.InitialQuantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0 +FairQueueingControlledDelay.MemoryLimit, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 +FairQueueingControlledDelay.Quantum, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0 +TokenBucketFilter.Burst, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 +TokenBucketFilter.LimitSize, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 255aaed51..d34155b19 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "alloc-util.h" #include "conf-files.h" @@ -15,13 +16,16 @@ #include "network-internal.h" #include "networkd-manager.h" #include "networkd-network.h" +#include "networkd-sriov.h" #include "parse-util.h" +#include "path-lookup.h" #include "set.h" #include "socket-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "tc.h" #include "util.h" /* Let's assume that anything above this number is a user misconfiguration. */ @@ -48,7 +52,7 @@ void network_apply_anonymize_if_set(Network *network) { /* RFC7844 section 3.6.: The client intending to protect its privacy SHOULD only request a minimal number of options in the PRL and SHOULD also randomly shuffle - the ordering of option codes in the PRL. If this random ordering + the ordering of option codes in the PRL. If this random ordering cannot be implemented, the client MAY order the option codes in the PRL by option code number (lowest to highest). */ @@ -154,7 +158,8 @@ int network_verify(Network *network) { Prefix *prefix, *prefix_next; Route *route, *route_next; FdbEntry *fdb, *fdb_next; - QDisc *qdisc; + TrafficControl *tc; + SRIOV *sr_iov; Iterator i; assert(network); @@ -163,14 +168,15 @@ int network_verify(Network *network) { if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) && strv_isempty(network->match_path) && strv_isempty(network->match_driver) && strv_isempty(network->match_type) && strv_isempty(network->match_name) && - strv_isempty(network->match_property) && strv_isempty(network->match_ssid) && !network->conditions) + strv_isempty(network->match_property) && strv_isempty(network->match_wlan_iftype) && + strv_isempty(network->match_ssid) && !network->conditions) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "%s: No valid settings found in the [Match] section, ignoring file. " "To match all interfaces, add Name=* in the [Match] section.", network->filename); /* skip out early if configuration does not match the environment */ - if (!condition_test_list(network->conditions, NULL, NULL, NULL)) + if (!condition_test_list(network->conditions, environ, NULL, NULL, NULL)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "%s: Conditions in the file do not match the system environment, skipping.", network->filename); @@ -265,6 +271,12 @@ int network_verify(Network *network) { network->dhcp_use_mtu = false; } + if (network->dhcp_use_gateway < 0) + network->dhcp_use_gateway = network->dhcp_use_routes; + + if (network->ignore_carrier_loss < 0) + network->ignore_carrier_loss = network->configure_without_carrier; + if (network->dhcp_critical >= 0) { if (network->keep_configuration >= 0) log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. " @@ -316,9 +328,13 @@ int network_verify(Network *network) { routing_policy_rule_free(rule); bool has_root = false, has_clsact = false; - ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i) - if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0) - qdisc_free(qdisc); + ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i) + if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0) + traffic_control_free(tc); + + ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i) + if (sr_iov_section_verify(sr_iov) < 0) + sr_iov_free(sr_iov); return 0; } @@ -326,7 +342,6 @@ int network_verify(Network *network) { int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) { _cleanup_free_ char *fname = NULL, *name = NULL; _cleanup_(network_unrefp) Network *network = NULL; - _cleanup_strv_free_ char **dropins = NULL; _cleanup_fclose_ FILE *file = NULL; const char *dropin_dirname; char *d; @@ -384,6 +399,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_use_dns = true, .dhcp_use_hostname = true, .dhcp_use_routes = true, + .dhcp_use_gateway = -1, /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */ .dhcp_send_hostname = true, .dhcp_send_release = true, @@ -400,12 +416,17 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp_use_timezone = false, .rapid_commit = true, + .dhcp6_route_metric = DHCP_ROUTE_METRIC, .dhcp6_use_ntp = true, .dhcp6_use_dns = true, - .dhcp_server_emit_dns = true, - .dhcp_server_emit_ntp = true, - .dhcp_server_emit_sip = true, + .dhcp6_pd_subnet_id = -1, + .dhcp6_pd_assign = true, + + .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true, + .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true, + .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true, + .dhcp_server_emit_router = true, .dhcp_server_emit_timezone = true, @@ -436,6 +457,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */ .link_local = _ADDRESS_FAMILY_INVALID, + .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID, + + .ipv4_accept_local = -1, .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO, .ipv6_accept_ra = -1, @@ -452,47 +476,72 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .ipv6_accept_ra_use_onlink_prefix = true, .ipv6_accept_ra_route_table = RT_TABLE_MAIN, .ipv6_accept_ra_route_table_set = false, + .ipv6_accept_ra_start_dhcp6_client = true, + .configure_without_carrier = false, + .ignore_carrier_loss = -1, .keep_configuration = _KEEP_CONFIGURATION_INVALID, - .can_triple_sampling = -1, + .can_termination = -1, .ip_service_type = -1, }; - r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, - "Match\0" - "Link\0" - "Network\0" - "Address\0" - "Neighbor\0" - "IPv6AddressLabel\0" - "RoutingPolicyRule\0" - "Route\0" - "NextHop\0" - "DHCP\0" /* compat */ - "DHCPv4\0" - "DHCPv6\0" - "DHCPServer\0" - "IPv6AcceptRA\0" - "IPv6NDPProxyAddress\0" - "Bridge\0" - "BridgeFDB\0" - "BridgeVLAN\0" - "IPv6PrefixDelegation\0" - "IPv6Prefix\0" - "IPv6RoutePrefix\0" - "TrafficControlQueueingDiscipline\0" - "CAN\0" - "QDisc\0" - "ControlledDelay\0" - "FairQueueing\0" - "FairQueueingControlledDelay\0" - "NetworkEmulator\0" - "StochasticFairnessQueueing\0" - "TokenBucketFilter\0" - "TrivialLinkEqualizer\0", - config_item_perf_lookup, network_network_gperf_lookup, - CONFIG_PARSE_WARN, network, &dropins); + r = config_parse_many( + filename, NETWORK_DIRS, dropin_dirname, + "Match\0" + "Link\0" + "SR-IOV\0" + "Network\0" + "Address\0" + "Neighbor\0" + "IPv6AddressLabel\0" + "RoutingPolicyRule\0" + "Route\0" + "NextHop\0" + "DHCP\0" /* compat */ + "DHCPv4\0" + "DHCPv6\0" + "DHCPv6PrefixDelegation\0" + "DHCPServer\0" + "IPv6AcceptRA\0" + "IPv6NDPProxyAddress\0" + "Bridge\0" + "BridgeFDB\0" + "BridgeVLAN\0" + "IPv6PrefixDelegation\0" + "IPv6Prefix\0" + "IPv6RoutePrefix\0" + "LLDP\0" + "TrafficControlQueueingDiscipline\0" + "CAN\0" + "QDisc\0" + "BFIFO\0" + "CAKE\0" + "ControlledDelay\0" + "DeficitRoundRobinScheduler\0" + "DeficitRoundRobinSchedulerClass\0" + "EnhancedTransmissionSelection\0" + "FairQueueing\0" + "FairQueueingControlledDelay\0" + "GenericRandomEarlyDetection\0" + "HeavyHitterFilter\0" + "HierarchyTokenBucket\0" + "HierarchyTokenBucketClass\0" + "NetworkEmulator\0" + "PFIFO\0" + "PFIFOFast\0" + "PFIFOHeadDrop\0" + "PIE\0" + "QuickFairQueueing\0" + "QuickFairQueueingClass\0" + "StochasticFairBlue\0" + "StochasticFairnessQueueing\0" + "TokenBucketFilter\0" + "TrivialLinkEqualizer\0", + config_item_perf_lookup, network_network_gperf_lookup, + CONFIG_PARSE_WARN, + network, + &network->timestamp); if (r < 0) return r; @@ -507,24 +556,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m", network->filename); - struct stat stats; - if (stat(filename, &stats) >= 0) - network->timestamp = timespec_load(&stats.st_mtim); - - char **f; - STRV_FOREACH(f, dropins) { - usec_t t; - - if (stat(*f, &stats) < 0) { - network->timestamp = 0; - break; - } - - t = timespec_load(&stats.st_mtim); - if (t > network->timestamp) - network->timestamp = t; - } - if (network_verify(network) < 0) /* Ignore .network files that do not match the conditions. */ return 0; @@ -636,25 +667,32 @@ static Network *network_free(Network *network) { free(network->description); free(network->dhcp_vendor_class_identifier); + free(network->dhcp_mudurl); strv_free(network->dhcp_user_class); free(network->dhcp_hostname); - set_free(network->dhcp_black_listed_ip); + set_free(network->dhcp_deny_listed_ip); + set_free(network->dhcp_allow_listed_ip); set_free(network->dhcp_request_options); + set_free(network->dhcp6_request_options); free(network->mac); + free(network->dhcp6_mudurl); + strv_free(network->dhcp6_user_class); + strv_free(network->dhcp6_vendor_class); if (network->dhcp_acd) sd_ipv4acd_unref(network->dhcp_acd); strv_free(network->ntp); + for (unsigned i = 0; i < network->n_dns; i++) + in_addr_full_free(network->dns[i]); free(network->dns); - strv_free(network->sip); ordered_set_free_free(network->search_domains); ordered_set_free_free(network->route_domains); strv_free(network->bind_carrier); ordered_set_free_free(network->router_search_domains); free(network->router_dns); - set_free_free(network->ndisc_black_listed_prefix); + set_free_free(network->ndisc_deny_listed_prefix); free(network->bridge_name); free(network->bond_name); @@ -704,7 +742,8 @@ static Network *network_free(Network *network) { hashmap_free(network->prefixes_by_section); hashmap_free(network->route_prefixes_by_section); hashmap_free(network->rules_by_section); - ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_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); if (network->manager && network->manager->duids_requesting_uuid) @@ -713,15 +752,21 @@ static Network *network_free(Network *network) { free(network->name); free(network->dhcp_server_timezone); - free(network->dhcp_server_dns); - free(network->dhcp_server_ntp); - free(network->dhcp_server_sip); + + for (sd_dhcp_lease_server_type t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++) + free(network->dhcp_server_emit[t].addresses); set_free_free(network->dnssec_negative_trust_anchors); + free(network->lldp_mud); + ordered_hashmap_free(network->dhcp_client_send_options); + ordered_hashmap_free(network->dhcp_client_send_vendor_options); ordered_hashmap_free(network->dhcp_server_send_options); - ordered_hashmap_free(network->ipv6_tokens); + ordered_hashmap_free(network->dhcp_server_send_vendor_options); + ordered_set_free(network->ipv6_tokens); + ordered_hashmap_free(network->dhcp6_client_send_options); + ordered_hashmap_free(network->dhcp6_client_send_vendor_options); return mfree(network); } @@ -860,7 +905,7 @@ int config_parse_stacked_netdev(const char *unit, NETDEV_KIND_XFRM)); if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -875,7 +920,7 @@ int config_parse_stacked_netdev(const char *unit, r = hashmap_put(*h, name, INT_TO_PTR(kind)); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Cannot add NetDev '%s' to network, ignoring assignment: %m", name); else if (r == 0) log_syntax(unit, LOG_DEBUG, filename, line, r, @@ -898,7 +943,6 @@ int config_parse_domains( void *data, void *userdata) { - const char *p; Network *n = data; int r; @@ -912,20 +956,21 @@ int config_parse_domains( return 0; } - p = rvalue; - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL, *normalized = NULL; const char *domain; bool is_route; r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue); - break; + return 0; } if (r == 0) - break; + return 0; is_route = w[0] == '~'; domain = is_route ? w + 1 : w; @@ -939,7 +984,7 @@ int config_parse_domains( } else { r = dns_name_normalize(domain, 0, &normalized); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain); continue; } @@ -947,7 +992,7 @@ int config_parse_domains( domain = normalized; if (is_localhost(domain)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s", domain); continue; @@ -957,14 +1002,12 @@ int config_parse_domains( OrderedSet **set = is_route ? &n->route_domains : &n->search_domains; r = ordered_set_ensure_allocated(set, &string_hash_ops); if (r < 0) - return r; + return log_oom(); r = ordered_set_put_strdup(*set, domain); if (r < 0) return log_oom(); } - - return 0; } int config_parse_ipv6token( @@ -990,19 +1033,19 @@ int config_parse_ipv6token( r = in_addr_from_string(AF_INET6, rvalue, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); return 0; } if (in_addr_is_null(AF_INET6, &buffer)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); return 0; } if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); return 0; } @@ -1045,7 +1088,7 @@ int config_parse_ipv6_privacy_extensions( if (streq(rvalue, "kernel")) s = _IPV6_PRIVACY_EXTENSIONS_INVALID; else { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); return 0; } @@ -1081,19 +1124,19 @@ int config_parse_hostname( return r; if (!hostname_is_valid(hn, false)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue); return 0; } r = dns_name_is_valid(hn); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + 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_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue); return 0; } @@ -1125,8 +1168,8 @@ int config_parse_timezone( if (r < 0) return r; - if (!timezone_is_valid(tz, LOG_ERR)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + if (!timezone_is_valid(tz, LOG_WARNING)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue); return 0; } @@ -1153,43 +1196,47 @@ int config_parse_dns( assert(lvalue); assert(rvalue); - for (;;) { - _cleanup_free_ char *w = NULL; - union in_addr_union a; - struct in_addr_data *m; - int family; + if (isempty(rvalue)) { + for (unsigned i = 0; i < n->n_dns; i++) + in_addr_full_free(n->dns[i]); + n->dns = mfree(n->dns); + n->n_dns = 0; + return 0; + } - r = extract_first_word(&rvalue, &w, NULL, 0); + for (const char *p = rvalue;;) { + _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL; + _cleanup_free_ char *w = NULL; + struct in_addr_full **m; + + r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - break; + return 0; } if (r == 0) - break; + return 0; - r = in_addr_from_string_auto(w, &family, &a); + r = in_addr_full_new_from_string(w, &dns); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse dns server address, ignoring: %s", w); continue; } - m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data)); + if (IN_SET(dns->port, 53, 853)) + dns->port = 0; + + m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*)); if (!m) return log_oom(); - m[n->n_dns++] = (struct in_addr_data) { - .family = family, - .address = a, - }; - + m[n->n_dns++] = TAKE_PTR(dns); n->dns = m; } - - return 0; } int config_parse_dnssec_negative_trust_anchors( @@ -1204,7 +1251,6 @@ int config_parse_dnssec_negative_trust_anchors( void *data, void *userdata) { - const char *p = rvalue; Network *n = data; int r; @@ -1217,37 +1263,31 @@ int config_parse_dnssec_negative_trust_anchors( return 0; } - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL; r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue); - break; + return 0; } if (r == 0) - break; + return 0; r = dns_name_is_valid(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "%s is not a valid domain name, ignoring.", w); continue; } - r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops); + r = set_ensure_consume(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops, TAKE_PTR(w)); if (r < 0) return log_oom(); - - r = set_put(n->dnssec_negative_trust_anchors, w); - if (r < 0) - return log_oom(); - if (r > 0) - w = NULL; } - - return 0; } int config_parse_ntp( @@ -1274,23 +1314,23 @@ int config_parse_ntp( return 0; } - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL; - r = extract_first_word(&rvalue, &w, NULL, 0); + r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue); - break; + return 0; } if (r == 0) - break; + return 0; r = dns_name_is_valid_or_address(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w); continue; } @@ -1299,15 +1339,13 @@ int config_parse_ntp( log_syntax(unit, LOG_WARNING, filename, line, 0, "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.", MAX_NTP_SERVERS, w); - break; + return 0; } r = strv_consume(l, TAKE_PTR(w)); if (r < 0) return log_oom(); } - - return 0; } int config_parse_required_for_online( @@ -1337,7 +1375,7 @@ int config_parse_required_for_online( if (r < 0) { r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue); return 0; @@ -1365,3 +1403,13 @@ 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"); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 4738a18be..5dcb3c548 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -17,6 +17,7 @@ #include "networkd-brvlan.h" #include "networkd-dhcp-common.h" #include "networkd-dhcp4.h" +#include "networkd-dhcp6.h" #include "networkd-dhcp-server.h" #include "networkd-fdb.h" #include "networkd-ipv6-proxy-ndp.h" @@ -30,8 +31,8 @@ #include "networkd-routing-policy-rule.h" #include "networkd-util.h" #include "ordered-set.h" -#include "qdisc.h" #include "resolve-util.h" +#include "socket-netlink.h" typedef enum IPv6PrivacyExtensions { /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ @@ -53,8 +54,23 @@ typedef enum KeepConfiguration { _KEEP_CONFIGURATION_INVALID = -1, } 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 = -1 +} IPv6LinkLocalAddressGenMode; + typedef struct Manager Manager; +typedef struct NetworkDHCPServerEmitAddress { + bool emit; + struct in_addr *addresses; + size_t n_addresses; +} NetworkDHCPServerEmitAddress; + struct Network { Manager *manager; @@ -92,11 +108,14 @@ struct Network { AddressFamily dhcp; DHCPClientIdentifier dhcp_client_identifier; char *dhcp_vendor_class_identifier; + char *dhcp_mudurl; char **dhcp_user_class; char *dhcp_hostname; uint64_t dhcp_max_attempts; uint32_t dhcp_route_metric; + bool dhcp_route_metric_set; uint32_t dhcp_route_table; + uint32_t dhcp_fallback_lease_lifetime; uint32_t dhcp_route_mtu; uint16_t dhcp_client_port; int dhcp_critical; @@ -105,11 +124,14 @@ struct Network { bool dhcp_send_hostname; bool dhcp_broadcast; bool dhcp_use_dns; + bool dhcp_use_dns_set; bool dhcp_routes_to_dns; bool dhcp_use_ntp; + bool dhcp_use_ntp_set; bool dhcp_use_sip; bool dhcp_use_mtu; bool dhcp_use_routes; + int dhcp_use_gateway; bool dhcp_use_timezone; bool rapid_commit; bool dhcp_use_hostname; @@ -118,32 +140,34 @@ struct Network { bool dhcp_send_decline; DHCPUseDomains dhcp_use_domains; sd_ipv4acd *dhcp_acd; - Set *dhcp_black_listed_ip; + Set *dhcp_deny_listed_ip; + Set *dhcp_allow_listed_ip; Set *dhcp_request_options; OrderedHashmap *dhcp_client_send_options; + OrderedHashmap *dhcp_client_send_vendor_options; OrderedHashmap *dhcp_server_send_options; + OrderedHashmap *dhcp_server_send_vendor_options; /* DHCPv6 Client support*/ bool dhcp6_use_dns; + bool dhcp6_use_dns_set; bool dhcp6_use_ntp; + bool dhcp6_use_ntp_set; uint8_t dhcp6_pd_length; + uint32_t dhcp6_route_metric; + bool dhcp6_route_metric_set; + char *dhcp6_mudurl; + char **dhcp6_user_class; + char **dhcp6_vendor_class; struct in6_addr dhcp6_pd_address; + DHCP6ClientStartMode dhcp6_without_ra; + OrderedHashmap *dhcp6_client_send_options; + OrderedHashmap *dhcp6_client_send_vendor_options; + Set *dhcp6_request_options; /* DHCP Server Support */ bool dhcp_server; - - bool dhcp_server_emit_dns; - struct in_addr *dhcp_server_dns; - unsigned n_dhcp_server_dns; - - bool dhcp_server_emit_ntp; - struct in_addr *dhcp_server_ntp; - unsigned n_dhcp_server_ntp; - - bool dhcp_server_emit_sip; - struct in_addr *dhcp_server_sip; - unsigned n_dhcp_server_sip; - + NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; bool dhcp_server_emit_router; bool dhcp_server_emit_timezone; char *dhcp_server_timezone; @@ -151,8 +175,9 @@ struct Network { uint32_t dhcp_server_pool_offset; uint32_t dhcp_server_pool_size; - /* IPV4LL Support */ + /* link local addressing support */ AddressFamily link_local; + IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode; bool ipv4ll_route; bool default_route_on_device; @@ -173,6 +198,11 @@ struct Network { RA flag is set, see RFC 7084, WPD-4 */ + /* DHCPv6 Prefix Delegation support */ + int64_t dhcp6_pd_subnet_id; + bool dhcp6_pd_assign; + union in_addr_union dhcp6_pd_token; + /* Bridge Support */ int use_bpdu; int hairpin; @@ -195,13 +225,20 @@ struct Network { uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; /* CAN support */ - uint64_t can_bitrate; + uint32_t can_bitrate; unsigned can_sample_point; + uint32_t can_data_bitrate; + unsigned can_data_sample_point; usec_t can_restart_us; int can_triple_sampling; + int can_termination; + int can_listen_only; + int can_fd_mode; + int can_non_iso; AddressFamily ip_forward; bool ip_masquerade; + int ipv4_accept_local; int ipv6_accept_ra; int ipv6_dad_transmits; @@ -215,22 +252,24 @@ struct Network { bool ipv6_accept_ra_use_onlink_prefix; bool active_slave; bool primary_slave; - DHCPUseDomains ipv6_accept_ra_use_domains; - uint32_t ipv6_accept_ra_route_table; bool ipv6_accept_ra_route_table_set; - Set *ndisc_black_listed_prefix; - OrderedHashmap *ipv6_tokens; + DHCPUseDomains ipv6_accept_ra_use_domains; + IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client; + uint32_t ipv6_accept_ra_route_table; + Set *ndisc_deny_listed_prefix; + OrderedSet *ipv6_tokens; IPv6PrivacyExtensions ipv6_privacy_extensions; struct ether_addr *mac; uint32_t mtu; + uint32_t group; int arp; int multicast; int allmulticast; bool unmanaged; bool configure_without_carrier; - bool ignore_carrier_loss; + int ignore_carrier_loss; KeepConfiguration keep_configuration; uint32_t iaid; DUID duid; @@ -240,8 +279,10 @@ struct Network { bool required_for_online; /* Is this network required to be considered online? */ LinkOperationalStateRange required_operstate_for_online; + /* LLDP support */ LLDPMode lldp_mode; /* LLDP reception */ LLDPEmit lldp_emit; /* LLDP transmission */ + char *lldp_mud; /* LLDP MUD URL */ LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); @@ -274,10 +315,11 @@ struct Network { Hashmap *prefixes_by_section; Hashmap *route_prefixes_by_section; Hashmap *rules_by_section; - OrderedHashmap *qdiscs_by_section; + OrderedHashmap *tc_by_section; + OrderedHashmap *sr_iov_by_section; /* All kinds of DNS configuration */ - struct in_addr_data *dns; + struct in_addr_full **dns; unsigned n_dns; OrderedSet *search_domains, *route_domains; @@ -289,7 +331,6 @@ struct Network { Set *dnssec_negative_trust_anchors; char **ntp; - char **sip; char **bind_carrier; }; @@ -325,6 +366,7 @@ 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_keep_configuration); +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length); @@ -333,3 +375,6 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; 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_; diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 45c13ca88..6d89be1a2 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -209,11 +209,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop nexthop->family = in->family; nexthop->gw = in->gw; - r = set_ensure_allocated(nexthops, &nexthop_hash_ops); - if (r < 0) - return r; - - r = set_put(*nexthops, nexthop); + r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop); if (r < 0) return r; if (r == 0) @@ -245,11 +241,7 @@ int nexthop_add(Link *link, NextHop *in, NextHop **ret) { return r; } else if (r == 0) { /* Take over a foreign nexthop */ - r = set_ensure_allocated(&link->nexthops, &nexthop_hash_ops); - if (r < 0) - return r; - - r = set_put(link->nexthops, nexthop); + r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop); if (r < 0) return r; @@ -422,11 +414,11 @@ int config_parse_nexthop_id( r = nexthop_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = safe_atou32(rvalue, &n->id); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -459,11 +451,11 @@ int config_parse_nexthop_gateway( r = nexthop_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = in_addr_from_string_auto(rvalue, &n->family, &n->gw); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 620939e25..e0c490bab 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -211,18 +211,19 @@ int config_parse_prefix(const char *unit, r = prefix_new_static(network, filename, section_line, &p); if (r < 0) - return r; + return log_oom(); r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); return 0; } - if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0) - return -EADDRNOTAVAIL; - - log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue); + r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue); + return 0; + } p = NULL; @@ -241,7 +242,7 @@ int config_parse_prefix_flags(const char *unit, void *userdata) { Network *network = userdata; _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; - int r, val; + int r; assert(filename); assert(section); @@ -251,22 +252,22 @@ int config_parse_prefix_flags(const char *unit, r = prefix_new_static(network, filename, section_line, &p); if (r < 0) - return r; + return log_oom(); r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); return 0; } - val = r; - if (streq(lvalue, "OnLink")) - r = sd_radv_prefix_set_onlink(p->radv_prefix, val); + r = sd_radv_prefix_set_onlink(p->radv_prefix, r); else if (streq(lvalue, "AddressAutoconfiguration")) - r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val); - if (r < 0) - return r; + r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue); + return 0; + } p = NULL; @@ -296,11 +297,11 @@ int config_parse_prefix_lifetime(const char *unit, r = prefix_new_static(network, filename, section_line, &p); if (r < 0) - return r; + return log_oom(); r = parse_sec(rvalue, &usec); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -311,14 +312,56 @@ int config_parse_prefix_lifetime(const char *unit, else if (streq(lvalue, "ValidLifetimeSec")) r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC)); - if (r < 0) - return r; + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue); + return 0; + } p = NULL; return 0; } +int config_parse_prefix_assign( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return log_oom(); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + p->assign = r; + p = NULL; + + return 0; +} + int config_parse_route_prefix(const char *unit, const char *filename, unsigned line, @@ -344,18 +387,19 @@ int config_parse_route_prefix(const char *unit, r = route_prefix_new_static(network, filename, section_line, &p); if (r < 0) - return r; + return log_oom(); r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue); return 0; } - if (sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen) < 0) - return -EADDRNOTAVAIL; - - log_syntax(unit, LOG_INFO, filename, line, r, "Found route prefix %s", rvalue); + r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m"); + return 0; + } p = NULL; @@ -385,19 +429,22 @@ int config_parse_route_prefix_lifetime(const char *unit, r = route_prefix_new_static(network, filename, section_line, &p); if (r < 0) - return r; + return log_oom(); r = parse_sec(rvalue, &usec); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Route lifetime is invalid, ignoring assignment: %s", rvalue); return 0; } /* a value of 0xffffffff represents infinity */ r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC)); - if (r < 0) - return r; + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to set route lifetime, ignoring assignment: %m"); + return 0; + } p = NULL; @@ -416,10 +463,10 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns, for (i = 0; i < network->n_dns; i++) { union in_addr_union *addr; - if (network->dns[i].family != AF_INET6) + if (network->dns[i]->family != AF_INET6) continue; - addr = &network->dns[i].address; + addr = &network->dns[i]->address; if (in_addr_is_null(AF_INET6, addr) || in_addr_is_link_local(AF_INET6, addr) || @@ -603,10 +650,7 @@ int radv_configure(Link *link) { return r; } - if (IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_STATIC, - RADV_PREFIX_DELEGATION_BOTH)) { - + if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) { LIST_FOREACH(prefixes, p, link->network->static_prefixes) { r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); if (r == -EEXIST) @@ -626,12 +670,42 @@ int radv_configure(Link *link) { if (r < 0) return r; } - } return 0; } +int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, + uint32_t lifetime_preferred, uint32_t lifetime_valid) { + _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; + int r; + + assert(link); + assert(link->radv); + + r = sd_radv_prefix_new(&p); + if (r < 0) + return r; + + r = sd_radv_prefix_set_prefix(p, prefix, prefix_len); + if (r < 0) + return r; + + r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); + if (r < 0) + return r; + + r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); + if (r < 0) + return r; + + r = sd_radv_add_prefix(link->radv, p, true); + if (r < 0 && r != -EEXIST) + return r; + + return 0; +} + int config_parse_radv_dns( const char *unit, const char *filename, @@ -645,14 +719,13 @@ int config_parse_radv_dns( void *userdata) { Network *n = data; - const char *p = rvalue; int r; assert(filename); assert(lvalue); assert(rvalue); - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL; union in_addr_union a; @@ -660,25 +733,25 @@ int config_parse_radv_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) - break; + return 0; if (streq(w, "_link_local")) a = IN_ADDR_NULL; else { r = in_addr_from_string(AF_INET6, w, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w); continue; } if (in_addr_is_null(AF_INET6, &a)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "DNS server address is null, ignoring: %s", w); continue; } @@ -692,8 +765,6 @@ int config_parse_radv_dns( m[n->n_router_dns++] = a.in6; n->router_dns = m; } - - return 0; } int config_parse_radv_search_domains( @@ -709,30 +780,29 @@ int config_parse_radv_search_domains( void *userdata) { Network *n = data; - const char *p = rvalue; int r; assert(filename); assert(lvalue); assert(rvalue); - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL, *idna = NULL; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) - break; + return 0; r = dns_name_apply_idna(w, &idna); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to apply IDNA to domain name '%s', ignoring: %m", w); continue; } else if (r == 0) @@ -741,14 +811,12 @@ int config_parse_radv_search_domains( r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); if (r < 0) - return r; + return log_oom(); r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); if (r < 0) - return r; + return log_oom(); } - - return 0; } static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = { @@ -763,37 +831,10 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN( RADVPrefixDelegation, RADV_PREFIX_DELEGATION_BOTH); -int config_parse_router_prefix_delegation( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - RADVPrefixDelegation d; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - d = radv_prefix_delegation_from_string(rvalue); - if (d < 0) { - log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue); - return 0; - } - - network->router_prefix_delegation = d; - - return 0; -} +DEFINE_CONFIG_PARSE_ENUM(config_parse_router_prefix_delegation, + radv_prefix_delegation, + RADVPrefixDelegation, + "Invalid router prefix delegation"); int config_parse_router_preference(const char *unit, const char *filename, @@ -820,7 +861,8 @@ int config_parse_router_preference(const char *unit, else if (streq(rvalue, "low")) network->router_preference = SD_NDISC_PREFERENCE_LOW; else - log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid router preference, ignoring assignment: %s", rvalue); return 0; } diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 21b323e83..496ef97ad 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -14,10 +14,10 @@ typedef struct Prefix Prefix; typedef struct RoutePrefix RoutePrefix; typedef enum RADVPrefixDelegation { - RADV_PREFIX_DELEGATION_NONE, - RADV_PREFIX_DELEGATION_STATIC, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH, + RADV_PREFIX_DELEGATION_NONE = 0, + RADV_PREFIX_DELEGATION_STATIC = 1 << 0, + RADV_PREFIX_DELEGATION_DHCP6 = 1 << 1, + RADV_PREFIX_DELEGATION_BOTH = RADV_PREFIX_DELEGATION_STATIC | RADV_PREFIX_DELEGATION_DHCP6, _RADV_PREFIX_DELEGATION_MAX, _RADV_PREFIX_DELEGATION_INVALID = -1, } RADVPrefixDelegation; @@ -28,6 +28,8 @@ struct Prefix { sd_radv_prefix *radv_prefix; + bool assign; + LIST_FIELDS(Prefix, prefixes); }; @@ -50,6 +52,8 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free); int radv_emit_dns(Link *link); int radv_configure(Link *link); +int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, + uint32_t lifetime_preferred, uint32_t lifetime_valid); const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_; @@ -59,6 +63,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_router_preference); CONFIG_PARSER_PROTOTYPE(config_parse_prefix); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags); CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime); +CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign); CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns); CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains); CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index de532eee7..541bf1e79 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -15,6 +15,7 @@ #include "socket-netlink.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "strxcpyx.h" #include "sysctl-util.h" #include "vrf.h" @@ -143,6 +144,14 @@ void route_free(Route *route) { if (route->link) { set_remove(route->link->routes, route); set_remove(route->link->routes_foreign, route); + set_remove(route->link->dhcp_routes, route); + set_remove(route->link->dhcp_routes_old, route); + set_remove(route->link->dhcp6_routes, route); + set_remove(route->link->dhcp6_routes_old, route); + set_remove(route->link->dhcp6_pd_routes, route); + set_remove(route->link->dhcp6_pd_routes_old, route); + set_remove(route->link->ndisc_routes, route); + set_remove(route->link->ndisc_routes_old, route); } ordered_set_free_free(route->multipath_routes); @@ -331,11 +340,7 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) route->initrwnd = in->initrwnd; route->lifetime = in->lifetime; - r = set_ensure_allocated(routes, &route_hash_ops); - if (r < 0) - return r; - - r = set_put(*routes, route); + r = set_ensure_put(routes, &route_hash_ops, route); if (r < 0) return r; if (r == 0) @@ -368,11 +373,7 @@ int route_add(Link *link, Route *in, Route **ret) { return r; } else if (r == 0) { /* Take over a foreign route */ - r = set_ensure_allocated(&link->routes, &route_hash_ops); - if (r < 0) - return r; - - r = set_put(link->routes, route); + r = set_ensure_put(&link->routes, &route_hash_ops, route); if (r < 0) return r; @@ -604,7 +605,8 @@ static int append_nexthops(Route *route, sd_netlink_message *req) { int route_configure( Route *route, Link *link, - link_netlink_message_handler_t callback) { + link_netlink_message_handler_t callback, + Route **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; @@ -810,6 +812,9 @@ int route_configure( sd_event_source_unref(route->expire); route->expire = TAKE_PTR(expire); + if (ret) + *ret = route; + return 1; } @@ -999,12 +1004,22 @@ int config_parse_gateway( /* we are not in an Route section, so treat * this as the special '0' section */ r = route_new_static(network, NULL, 0, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } } else { r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } if (streq(rvalue, "_dhcp")) { n->gateway_from_dhcp = true; @@ -1018,7 +1033,7 @@ int config_parse_gateway( else r = in_addr_from_string(n->family, rvalue, &n->gw); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -1050,15 +1065,20 @@ int config_parse_preferred_src( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } if (n->family == AF_UNSPEC) r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc); else r = in_addr_from_string(n->family, rvalue, &n->prefsrc); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, + log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -1092,8 +1112,13 @@ int config_parse_destination( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } if (streq(lvalue, "Destination")) { buffer = &n->dst; @@ -1109,7 +1134,7 @@ int config_parse_destination( else r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, + log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -1141,12 +1166,17 @@ int config_parse_route_priority( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = safe_atou32(rvalue, &n->priority); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1178,12 +1208,17 @@ int config_parse_route_scope( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = route_scope_from_string(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route scope: %s", rvalue); return 0; } @@ -1216,8 +1251,13 @@ int config_parse_route_table( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = route_table_from_string(rvalue); if (r >= 0) @@ -1225,7 +1265,7 @@ int config_parse_route_table( else { r = safe_atou32(rvalue, &n->table); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1236,7 +1276,7 @@ int config_parse_route_table( return 0; } -int config_parse_gateway_onlink( +int config_parse_route_boolean( const char *unit, const char *filename, unsigned line, @@ -1259,17 +1299,31 @@ int config_parse_gateway_onlink( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue); return 0; } - n->gateway_onlink = r; + if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink")) + n->gateway_onlink = r; + else if (streq(lvalue, "QuickAck")) + n->quickack = r; + else if (streq(lvalue, "FastOpenNoCookie")) + n->fast_open_no_cookie = r; + else if (streq(lvalue, "TTLPropagate")) + n->ttl_propagate = r; + else + assert_not_reached("Invalid lvalue"); TAKE_PTR(n); return 0; @@ -1292,8 +1346,13 @@ int config_parse_ipv6_route_preference( int r; r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } if (streq(rvalue, "low")) n->pref = ICMPV6_ROUTER_PREF_LOW; @@ -1302,7 +1361,7 @@ int config_parse_ipv6_route_preference( else if (streq(rvalue, "high")) n->pref = ICMPV6_ROUTER_PREF_HIGH; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue); return 0; } @@ -1327,8 +1386,13 @@ int config_parse_route_protocol( int r; r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = route_protocol_from_string(rvalue); if (r >= 0) @@ -1336,7 +1400,7 @@ int config_parse_route_protocol( else { r = safe_atou8(rvalue , &n->protocol); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1363,12 +1427,17 @@ int config_parse_route_type( int t, r; r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } t = route_type_from_string(rvalue); if (t < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1403,17 +1472,22 @@ int config_parse_tcp_window( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = parse_size(rvalue, 1024, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue); return 0; } if (k > UINT32_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -1429,82 +1503,6 @@ int config_parse_tcp_window( return 0; } -int config_parse_quickack( - 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_(route_free_or_set_invalidp) Route *n = NULL; - Network *network = userdata; - int k, r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, - "Failed to parse TCP quickack, ignoring: %s", rvalue); - return 0; - } - - n->quickack = !!k; - TAKE_PTR(n); - return 0; -} - -int config_parse_fast_open_no_cookie( - 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_(route_free_or_set_invalidp) Route *n = NULL; - Network *network = userdata; - int k, r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, - "Failed to parse TCP fastopen no cookie, ignoring: %s", rvalue); - return 0; - } - - n->fast_open_no_cookie = k; - TAKE_PTR(n); - return 0; -} - int config_parse_route_mtu( const char *unit, const char *filename, @@ -1528,8 +1526,13 @@ int config_parse_route_mtu( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata); if (r < 0) @@ -1539,45 +1542,6 @@ int config_parse_route_mtu( return 0; } -int config_parse_route_ttl_propagate( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - _cleanup_(route_free_or_set_invalidp) Route *n = NULL; - int r, k; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, - "Failed to parse TTLPropagate= value, ignoring: %s", rvalue); - return 0; - } - - n->ttl_propagate = k; - - TAKE_PTR(n); - return 0; -} - int config_parse_multipath_route( const char *unit, const char *filename, @@ -1605,8 +1569,13 @@ int config_parse_multipath_route( assert(data); r = route_new_static(network, filename, section_line, &n); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } if (isempty(rvalue)) { n->multipath_routes = ordered_set_free_free(n->multipath_routes); @@ -1622,7 +1591,7 @@ int config_parse_multipath_route( if (r == -ENOMEM) return log_oom(); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid multipath route option, ignoring assignment: %s", rvalue); return 0; } @@ -1639,7 +1608,7 @@ int config_parse_multipath_route( r = in_addr_from_string_auto(ip, &family, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue); return 0; } @@ -1649,7 +1618,7 @@ int config_parse_multipath_route( if (dev) { r = resolve_interface(NULL, dev); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid interface name or index, ignoring assignment: %s", dev); return 0; } @@ -1659,12 +1628,12 @@ int config_parse_multipath_route( if (!isempty(p)) { r = safe_atou32(p, &m->weight); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid multipath route weight, ignoring assignment: %s", p); return 0; } if (m->weight == 0 || m->weight > 256) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid multipath route weight, ignoring assignment: %s", p); return 0; } @@ -1676,7 +1645,7 @@ int config_parse_multipath_route( r = ordered_set_put(n->multipath_routes, m); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to store multipath route, ignoring assignment: %m"); return 0; } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 067c65f2f..3beee9b03 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -66,7 +66,7 @@ extern const struct hash_ops route_hash_ops; int route_new(Route **ret); void route_free(Route *route); -int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback); +int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret); int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback); int route_get(Link *link, Route *in, Route **ret); @@ -100,13 +100,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_destination); CONFIG_PARSER_PROTOTYPE(config_parse_route_priority); CONFIG_PARSER_PROTOTYPE(config_parse_route_scope); CONFIG_PARSER_PROTOTYPE(config_parse_route_table); -CONFIG_PARSER_PROTOTYPE(config_parse_gateway_onlink); +CONFIG_PARSER_PROTOTYPE(config_parse_route_boolean); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_route_preference); CONFIG_PARSER_PROTOTYPE(config_parse_route_protocol); CONFIG_PARSER_PROTOTYPE(config_parse_route_type); CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window); -CONFIG_PARSER_PROTOTYPE(config_parse_quickack); -CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie); -CONFIG_PARSER_PROTOTYPE(config_parse_route_ttl_propagate); CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu); CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 641f88408..36dad527d 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -132,11 +132,8 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash24_compress(&rule->dport, sizeof(rule->dport), state); siphash24_compress(&rule->uid_range, sizeof(rule->uid_range), state); - if (rule->iif) - siphash24_compress(rule->iif, strlen(rule->iif), state); - - if (rule->oif) - siphash24_compress(rule->oif, strlen(rule->oif), state); + siphash24_compress_string(rule->iif, state); + siphash24_compress_string(rule->oif, state); break; default: @@ -230,7 +227,12 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro } } -DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + routing_policy_rule_hash_ops, + RoutingPolicyRule, + routing_policy_rule_hash_func, + routing_policy_rule_compare_func, + routing_policy_rule_free); int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) { @@ -263,11 +265,7 @@ int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) { if (set_contains(m->rules_foreign, rule)) { set_remove(m->rules_foreign, rule); - r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops); - if (r < 0) - return r; - - r = set_put(m->rules, rule); + r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule); if (r < 0) return r; if (r == 0) @@ -295,11 +293,7 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli if (r < 0) return r; - r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops); - if (r < 0) - return r; - - r = set_put(*rules, rule); + r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule); if (r < 0) return r; if (r == 0) @@ -514,17 +508,17 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl 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 log_link_error_errno(link, r, "Could not set IP rule TOS: %m"); } 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 log_link_error_errno(link, r, "Could not set IP rule table: %m"); } 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 log_link_error_errno(link, r, "Could not set IP rule table: %m"); r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table); if (r < 0) @@ -702,11 +696,11 @@ int config_parse_routing_policy_rule_tos( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = safe_atou8(rvalue, &n->tos); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule TOS, ignoring: %s", rvalue); return 0; } @@ -739,11 +733,11 @@ int config_parse_routing_policy_rule_priority( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = safe_atou32(rvalue, &n->priority); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue); return 0; } @@ -776,11 +770,11 @@ int config_parse_routing_policy_rule_table( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = safe_atou32(rvalue, &n->table); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue); return 0; } @@ -813,11 +807,11 @@ int config_parse_routing_policy_rule_fwmark_mask( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue); return 0; } @@ -852,7 +846,7 @@ int config_parse_routing_policy_rule_prefix( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); if (streq(lvalue, "To")) { buffer = &n->to; @@ -867,7 +861,7 @@ int config_parse_routing_policy_rule_prefix( else r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -900,10 +894,10 @@ int config_parse_routing_policy_rule_device( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue); return 0; } @@ -946,11 +940,11 @@ int config_parse_routing_policy_rule_port_range( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = parse_ip_port_range(rvalue, &low, &high); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue); return 0; } @@ -991,11 +985,11 @@ int config_parse_routing_policy_rule_ip_protocol( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = parse_ip_protocol(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue); return 0; } @@ -1030,11 +1024,11 @@ int config_parse_routing_policy_rule_invert( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue); return 0; } @@ -1070,11 +1064,11 @@ int config_parse_routing_policy_rule_family( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); a = routing_policy_rule_address_family_from_string(rvalue); if (a < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid address family '%s', ignoring.", rvalue); return 0; } @@ -1110,7 +1104,7 @@ int config_parse_routing_policy_rule_uid_range( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = get_user_creds(&rvalue, &start, NULL, NULL, NULL, 0); if (r >= 0) @@ -1118,7 +1112,7 @@ int config_parse_routing_policy_rule_uid_range( else { r = parse_uid_range(rvalue, &start, &end); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid uid or uid range '%s', ignoring: %m", rvalue); return 0; } @@ -1155,15 +1149,15 @@ int config_parse_routing_policy_rule_suppress_prefixlen( r = routing_policy_rule_new_static(network, filename, section_line, &n); if (r < 0) - return r; + return log_oom(); r = parse_ip_prefix_length(rvalue, &n->suppress_prefixlen); if (r == -ERANGE) { - log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue); return 0; } if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue); return 0; } @@ -1328,10 +1322,6 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { if (!l) return -ENOMEM; - r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops); - if (r < 0) - return r; - STRV_FOREACH(i, l) { _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; @@ -1384,7 +1374,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { } else if (streq(a, "tos")) { r = safe_atou8(b, &rule->tos); if (r < 0) { - log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b); + log_error_errno(r, "Failed to parse RPDB rule TOS, ignoring: %s", b); continue; } } else if (streq(a, "table")) { @@ -1461,7 +1451,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { } } - r = set_put(*rules, rule); + r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule); if (r < 0) { log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p); continue; diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c new file mode 100644 index 000000000..7d9970734 --- /dev/null +++ b/src/network/networkd-sriov.c @@ -0,0 +1,501 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include "alloc-util.h" +#include "netlink-util.h" +#include "networkd-manager.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_t) -1, + .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_allocated(&network->sr_iov_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(network->sr_iov_by_section, 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) { + int r; + + 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) { + log_link_message_error_errno(link, m, r, "Could not set up SR-IOV"); + link_enter_failed(link); + return 1; + } + + if (link->sr_iov_messages == 0) { + log_link_debug(link, "SR-IOV configured"); + link->sr_iov_configured = true; + link_check_ready(link); + } + + return 1; +} + +int sr_iov_configure(Link *link, SRIOV *sr_iov) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + + 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); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST); + if (r < 0) + return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m"); + + 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; +} + +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_t) -1) + 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; +} + +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_t) -1; + else if (streq(lvalue, "VLANId")) + sr_iov->vlan = 0; + else if (streq(lvalue, "QualityOfService")) + sr_iov->qos = 0; + else + assert_not_reached("Invalid lvalue"); + + 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("Invalid lvalue"); + + 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("Invalid lvalue"); + + 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("Invalid lvalue"); + + 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 = ether_addr_from_string(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 new file mode 100644 index 000000000..a545d1292 --- /dev/null +++ b/src/network/networkd-sriov.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include + +#include "conf-parser.h" +#include "networkd-link.h" +#include "networkd-network.h" +#include "networkd-util.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 = -1, +} 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 sr_iov_configure(Link *link, SRIOV *sr_iov); +int sr_iov_section_verify(SRIOV *sr_iov); + +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); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index deba99537..ce9319d94 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -8,14 +8,14 @@ #include "string-util.h" #include "util.h" -static const char * const address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char* const address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "no", [ADDRESS_FAMILY_YES] = "yes", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char * const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char* const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "no", [ADDRESS_FAMILY_YES] = "yes", [ADDRESS_FAMILY_IPV4] = "ipv4", @@ -24,25 +24,35 @@ static const char * const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = [ADDRESS_FAMILY_FALLBACK_IPV4] = "ipv4-fallback", }; -static const char * const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char* const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; -static const char * const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = { +static const char* const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = { [ADDRESS_FAMILY_NO] = "none", [ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV6] = "ipv6", }; +static const char* const dhcp_lease_server_type_table[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = { + [SD_DHCP_LEASE_DNS] = "DNS servers", + [SD_DHCP_LEASE_NTP] = "NTP servers", + [SD_DHCP_LEASE_SIP] = "SIP servers", + [SD_DHCP_LEASE_POP3] = "POP3 servers", + [SD_DHCP_LEASE_SMTP] = "SMTP servers", + [SD_DHCP_LEASE_LPR] = "LPR servers", +}; + DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily); DEFINE_STRING_TABLE_LOOKUP(duplicate_address_detection_address_family, AddressFamily); DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family, AddressFamily, "Failed to parse option"); +DEFINE_STRING_TABLE_LOOKUP(dhcp_lease_server_type, sd_dhcp_lease_server_type); int config_parse_address_family_with_kernel( const char* unit, @@ -76,7 +86,7 @@ int config_parse_address_family_with_kernel( if (streq(rvalue, "kernel")) s = ADDRESS_FAMILY_NO; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue); return 0; } } @@ -97,7 +107,7 @@ int kernel_route_expiration_supported(void) { .type = CONDITION_KERNEL_VERSION, .parameter = (char *) ">= 4.5" }; - r = condition_test(&c); + r = condition_test(&c, NULL); if (r < 0) return r; @@ -107,7 +117,7 @@ int kernel_route_expiration_supported(void) { } static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) { - siphash24_compress(c->filename, strlen(c->filename), state); + siphash24_compress_string(c->filename, state); siphash24_compress(&c->line, sizeof(c->line), state); } diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 28dd9d3fe..0433f883a 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "sd-dhcp-lease.h" + #include "conf-parser.h" #include "hash-funcs.h" #include "macro.h" @@ -38,6 +40,9 @@ AddressFamily routing_policy_rule_address_family_from_string(const char *s) _pur const char *duplicate_address_detection_address_family_to_string(AddressFamily b) _const_; AddressFamily duplicate_address_detection_address_family_from_string(const char *s) _pure_; +const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type t) _const_; +sd_dhcp_lease_server_type dhcp_lease_server_type_from_string(const char *s) _pure_; + int kernel_route_expiration_supported(void); int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s); diff --git a/src/network/networkd.c b/src/network/networkd.c index 35f22fea8..445aee16a 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -17,8 +17,8 @@ #include "user-util.h" static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; int r; log_setup_service(); diff --git a/src/network/networkd.conf b/src/network/networkd.conf index c5667da9f..5339e5e5e 100644 --- a/src/network/networkd.conf +++ b/src/network/networkd.conf @@ -14,6 +14,7 @@ [Network] #SpeedMeter=no #SpeedMeterIntervalSec=10sec +#ManageForeignRoutes=yes [DHCP] #DUIDType=vendor diff --git a/src/network/org.freedesktop.network1.policy b/src/network/org.freedesktop.network1.policy index 9b1895e65..50b0ef081 100644 --- a/src/network/org.freedesktop.network1.policy +++ b/src/network/org.freedesktop.network1.policy @@ -139,6 +139,17 @@ unix-user:systemd-network + + DHCP server sends force renew message + Authentication is required to send force renew message. + + auth_admin + auth_admin + auth_admin_keep + + unix-user:systemd-network + + Renew dynamic addresses Authentication is required to renew dynamic addresses. diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c new file mode 100644 index 000000000..1da1ec40c --- /dev/null +++ b/src/network/tc/cake.c @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "cake.h" +#include "conf-parser.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "string-util.h" + +static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + CommonApplicationsKeptEnhanced *c; + int r; + + assert(link); + assert(qdisc); + assert(req); + + 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"); + + 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"); + } + + 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"); + + 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 0; +} + +int config_parse_cake_bandwidth( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + CommonApplicationsKeptEnhanced *c; + Network *network = data; + uint64_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->bandwidth = 0; + + qdisc = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->bandwidth = k/8; + qdisc = NULL; + + return 0; +} + +int config_parse_cake_overhead( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + CommonApplicationsKeptEnhanced *c; + Network *network = data; + int32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->overhead = 0; + qdisc = NULL; + return 0; + } + + r = safe_atoi32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (v < -64 || v > 256) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->overhead = v; + qdisc = NULL; + return 0; +} + +const QDiscVTable cake_vtable = { + .object_size = sizeof(CommonApplicationsKeptEnhanced), + .tca_kind = "cake", + .fill_message = cake_fill_message, +}; diff --git a/src/network/tc/cake.h b/src/network/tc/cake.h new file mode 100644 index 000000000..36de5110d --- /dev/null +++ b/src/network/tc/cake.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct CommonApplicationsKeptEnhanced { + QDisc meta; + + int overhead; + uint64_t bandwidth; + +} CommonApplicationsKeptEnhanced; + +DEFINE_QDISC_CAST(CAKE, CommonApplicationsKeptEnhanced); +extern const QDiscVTable cake_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_cake_bandwidth); +CONFIG_PARSER_PROTOTYPE(config_parse_cake_overhead); diff --git a/src/network/tc/codel.c b/src/network/tc/codel.c index 65c724da7..cba6faf37 100644 --- a/src/network/tc/codel.c +++ b/src/network/tc/codel.c @@ -99,9 +99,11 @@ int config_parse_controlled_delay_u32( r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } cd = CODEL(qdisc); @@ -114,7 +116,7 @@ int config_parse_controlled_delay_u32( r = safe_atou32(rvalue, &cd->packet_limit); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -151,9 +153,11 @@ int config_parse_controlled_delay_usec( r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } cd = CODEL(qdisc); @@ -178,7 +182,7 @@ int config_parse_controlled_delay_usec( r = parse_sec(rvalue, p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -214,9 +218,11 @@ int config_parse_controlled_delay_bool( r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } cd = CODEL(qdisc); @@ -229,7 +235,7 @@ int config_parse_controlled_delay_bool( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/drr.c b/src/network/tc/drr.c new file mode 100644 index 000000000..9810a120d --- /dev/null +++ b/src/network/tc/drr.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "drr.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" + +const QDiscVTable drr_vtable = { + .object_size = sizeof(DeficitRoundRobinScheduler), + .tca_kind = "drr", +}; + +static int drr_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { + DeficitRoundRobinSchedulerClass *drr; + int r; + + assert(link); + assert(tclass); + assert(req); + + 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"); + + 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"); + } + + 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 0; +} + +int config_parse_drr_size( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + DeficitRoundRobinSchedulerClass *drr; + Network *network = data; + uint64_t u; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_DRR, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + drr = TCLASS_TO_DRR(tclass); + + if (isempty(rvalue)) { + drr->quantum = 0; + + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1024, &u); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (u > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + drr->quantum = (uint32_t) u; + + tclass = NULL; + return 0; +} + +const TClassVTable drr_tclass_vtable = { + .object_size = sizeof(DeficitRoundRobinSchedulerClass), + .tca_kind = "drr", + .fill_message = drr_class_fill_message, +}; diff --git a/src/network/tc/drr.h b/src/network/tc/drr.h new file mode 100644 index 000000000..ff2b658ac --- /dev/null +++ b/src/network/tc/drr.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "qdisc.h" + +typedef struct DeficitRoundRobinScheduler { + QDisc meta; +} DeficitRoundRobinScheduler; + +DEFINE_QDISC_CAST(DRR, DeficitRoundRobinScheduler); +extern const QDiscVTable drr_vtable; + +typedef struct DeficitRoundRobinSchedulerClass { + TClass meta; + + uint32_t quantum; +} DeficitRoundRobinSchedulerClass; + +DEFINE_TCLASS_CAST(DRR, DeficitRoundRobinSchedulerClass); +extern const TClassVTable drr_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_drr_size); diff --git a/src/network/tc/ets.c b/src/network/tc/ets.c new file mode 100644 index 000000000..ece1f369d --- /dev/null +++ b/src/network/tc/ets.c @@ -0,0 +1,344 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "ets.h" +#include "memory-util.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "string-util.h" +#include "tc-util.h" + +static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + EnhancedTransmissionSelection *ets; + int r; + + assert(link); + assert(qdisc); + assert(req); + + 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"); + + 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"); + + 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"); + } + + 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"); + + 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"); + } + + 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"); + } + + 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"); + + 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"); + } + + 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"); + } + + 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 0; +} + +int config_parse_ets_u8( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + EnhancedTransmissionSelection *ets; + Network *network = data; + uint8_t v, *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + ets = ETS(qdisc); + if (streq(lvalue, "Bands")) + p = &ets->n_bands; + else if (streq(lvalue, "StrictBands")) + p = &ets->n_strict; + else + assert_not_reached("Invalid lvalue."); + + if (isempty(rvalue)) { + *p = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou8(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (v > TCQ_ETS_MAX_BANDS) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s='. The value must be <= %d, ignoring assignment: %s", + lvalue, TCQ_ETS_MAX_BANDS, rvalue); + return 0; + } + + *p = v; + qdisc = NULL; + + return 0; +} + +int config_parse_ets_quanta( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + EnhancedTransmissionSelection *ets; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + ets = ETS(qdisc); + + if (isempty(rvalue)) { + memzero(ets->quanta, sizeof(uint32_t) * TCQ_ETS_MAX_BANDS); + ets->n_quanta = 0; + + qdisc = NULL; + return 0; + } + + for (const char *p = rvalue;;) { + _cleanup_free_ char *word = NULL; + uint64_t v; + + r = extract_first_word(&p, &word, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to extract next value, ignoring: %m"); + break; + } + if (r == 0) + break; + + r = parse_size(word, 1024, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, word); + continue; + } + if (v == 0 || v > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, word); + continue; + } + if (ets->n_quanta >= TCQ_ETS_MAX_BANDS) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Too many quanta in '%s=', ignoring assignment: %s", + lvalue, word); + continue; + } + + ets->quanta[ets->n_quanta++] = v; + } + + qdisc = NULL; + + return 0; +} + +int config_parse_ets_prio( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + EnhancedTransmissionSelection *ets; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + ets = ETS(qdisc); + + if (isempty(rvalue)) { + memzero(ets->prio, sizeof(uint8_t) * (TC_PRIO_MAX + 1)); + ets->n_prio = 0; + + qdisc = NULL; + return 0; + } + + for (const char *p = rvalue;;) { + _cleanup_free_ char *word = NULL; + uint8_t v; + + r = extract_first_word(&p, &word, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to extract next value, ignoring: %m"); + break; + } + if (r == 0) + break; + + r = safe_atou8(word, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, word); + continue; + } + if (ets->n_prio > TC_PRIO_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Too many priomap in '%s=', ignoring assignment: %s", + lvalue, word); + continue; + } + + ets->prio[ets->n_prio++] = v; + } + + qdisc = NULL; + + return 0; +} + +static int enhanced_transmission_selection_verify(QDisc *qdisc) { + EnhancedTransmissionSelection *ets; + + assert(qdisc); + + ets = ETS(qdisc); + + if (ets->n_bands == 0) + ets->n_bands = ets->n_strict + ets->n_quanta; + + if (ets->n_bands == 0) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: At least one of Band=, Strict=, or Quanta= must be specified. " + "Ignoring [EnhancedTransmissionSelection] section from line %u.", + qdisc->section->filename, qdisc->section->line); + + if (ets->n_bands < ets->n_strict + ets->n_quanta) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Not enough total bands to cover all the strict bands and quanta. " + "Ignoring [EnhancedTransmissionSelection] section from line %u.", + qdisc->section->filename, qdisc->section->line); + + for (unsigned i = 0; i < ets->n_prio; i++) + if (ets->prio[i] >= ets->n_bands) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: PriorityMap= element is out of bands. " + "Ignoring [EnhancedTransmissionSelection] section from line %u.", + qdisc->section->filename, qdisc->section->line); + + return 0; +} + +const QDiscVTable ets_vtable = { + .object_size = sizeof(EnhancedTransmissionSelection), + .tca_kind = "ets", + .fill_message = enhanced_transmission_selection_fill_message, + .verify = enhanced_transmission_selection_verify, +}; diff --git a/src/network/tc/ets.h b/src/network/tc/ets.h new file mode 100644 index 000000000..c35d5976d --- /dev/null +++ b/src/network/tc/ets.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct EnhancedTransmissionSelection { + QDisc meta; + + uint8_t n_bands; + uint8_t n_strict; + unsigned n_quanta; + uint32_t quanta[TCQ_ETS_MAX_BANDS]; + unsigned n_prio; + uint8_t prio[TC_PRIO_MAX + 1]; +} EnhancedTransmissionSelection; + +DEFINE_QDISC_CAST(ETS, EnhancedTransmissionSelection); +extern const QDiscVTable ets_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_ets_u8); +CONFIG_PARSER_PROTOTYPE(config_parse_ets_quanta); +CONFIG_PARSER_PROTOTYPE(config_parse_ets_prio); diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c new file mode 100644 index 000000000..e955223a8 --- /dev/null +++ b/src/network/tc/fifo.c @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "fifo.h" +#include "netlink-util.h" +#include "parse-util.h" +#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; + + assert(link); + assert(qdisc); + assert(req); + + switch(qdisc->kind) { + case QDISC_KIND_PFIFO: + fifo = PFIFO(qdisc); + break; + case QDISC_KIND_BFIFO: + fifo = BFIFO(qdisc); + break; + case QDISC_KIND_PFIFO_HEAD_DROP: + fifo = PFIFO_HEAD_DROP(qdisc); + break; + default: + assert_not_reached("Invalid QDisc kind."); + } + + opt.limit = fifo->limit; + + r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_fifo_qopt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m"); + + return 0; +} + +int config_parse_pfifo_size( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + Network *network = data; + FirstInFirstOut *fifo; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + switch(qdisc->kind) { + case QDISC_KIND_PFIFO: + fifo = PFIFO(qdisc); + break; + case QDISC_KIND_PFIFO_HEAD_DROP: + fifo = PFIFO_HEAD_DROP(qdisc); + break; + default: + assert_not_reached("Invalid QDisc kind."); + } + + if (isempty(rvalue)) { + fifo->limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &fifo->limit); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + return 0; +} + +int config_parse_bfifo_size( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + Network *network = data; + FirstInFirstOut *fifo; + uint64_t u; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_BFIFO, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + fifo = BFIFO(qdisc); + + if (isempty(rvalue)) { + fifo->limit = 0; + + qdisc = NULL; + return 0; + } + + r = parse_size(rvalue, 1024, &u); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (u > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + fifo->limit = (uint32_t) u; + + qdisc = NULL; + return 0; +} + +const QDiscVTable pfifo_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "pfifo", + .fill_message = fifo_fill_message, +}; + +const QDiscVTable bfifo_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "bfifo", + .fill_message = fifo_fill_message, +}; + +const QDiscVTable pfifo_head_drop_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "pfifo_head_drop", + .fill_message = fifo_fill_message, +}; + +const QDiscVTable pfifo_fast_vtable = { + .object_size = sizeof(FirstInFirstOut), + .tca_kind = "pfifo_fast", +}; diff --git a/src/network/tc/fifo.h b/src/network/tc/fifo.h new file mode 100644 index 000000000..e4c976b94 --- /dev/null +++ b/src/network/tc/fifo.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct FirstInFirstOut { + QDisc meta; + + uint32_t limit; +} FirstInFirstOut; + +DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut); +DEFINE_QDISC_CAST(BFIFO, FirstInFirstOut); +DEFINE_QDISC_CAST(PFIFO_HEAD_DROP, FirstInFirstOut); +DEFINE_QDISC_CAST(PFIFO_FAST, FirstInFirstOut); + +extern const QDiscVTable pfifo_vtable; +extern const QDiscVTable bfifo_vtable; +extern const QDiscVTable pfifo_head_drop_vtable; +extern const QDiscVTable pfifo_fast_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_pfifo_size); +CONFIG_PARSER_PROTOTYPE(config_parse_bfifo_size); diff --git a/src/network/tc/fq-codel.c b/src/network/tc/fq-codel.c index 6c7932c70..17a291514 100644 --- a/src/network/tc/fq-codel.c +++ b/src/network/tc/fq-codel.c @@ -9,6 +9,7 @@ #include "parse-util.h" #include "qdisc.h" #include "string-util.h" +#include "strv.h" static int fair_queueing_controlled_delay_init(QDisc *qdisc) { FairQueueingControlledDelay *fqcd; @@ -119,9 +120,11 @@ int config_parse_fair_queueing_controlled_delay_u32( r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fqcd = FQ_CODEL(qdisc); @@ -141,7 +144,7 @@ int config_parse_fair_queueing_controlled_delay_u32( r = safe_atou32(rvalue, p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -178,9 +181,11 @@ int config_parse_fair_queueing_controlled_delay_usec( r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fqcd = FQ_CODEL(qdisc); @@ -205,7 +210,7 @@ int config_parse_fair_queueing_controlled_delay_usec( r = parse_sec(rvalue, p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -241,9 +246,11 @@ int config_parse_fair_queueing_controlled_delay_bool( r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fqcd = FQ_CODEL(qdisc); @@ -256,7 +263,7 @@ int config_parse_fair_queueing_controlled_delay_bool( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -295,21 +302,23 @@ int config_parse_fair_queueing_controlled_delay_size( r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fqcd = FQ_CODEL(qdisc); - if (streq(lvalue, "MemoryLimit")) + if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit")) p = &fqcd->memory_limit; - else if (streq(lvalue, "Quantum")) + else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum")) p = &fqcd->quantum; else assert_not_reached("Invalid lvalue."); if (isempty(rvalue)) { - if (streq(lvalue, "MemoryLimit")) + if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit")) *p = UINT32_MAX; else *p = 0; @@ -320,13 +329,13 @@ int config_parse_fair_queueing_controlled_delay_size( r = parse_size(rvalue, 1024, &sz); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } if (sz >= UINT32_MAX) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified '%s=' is too large, ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/fq.c b/src/network/tc/fq.c index c7eeec230..d3218203b 100644 --- a/src/network/tc/fq.c +++ b/src/network/tc/fq.c @@ -9,7 +9,7 @@ #include "netlink-util.h" #include "parse-util.h" #include "string-util.h" -#include "util.h" +#include "strv.h" static int fair_queueing_init(QDisc *qdisc) { FairQueueing *fq; @@ -128,9 +128,11 @@ int config_parse_fair_queueing_u32( r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fq = FQ(qdisc); @@ -154,7 +156,7 @@ int config_parse_fair_queueing_u32( r = safe_atou32(rvalue, p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -192,15 +194,17 @@ int config_parse_fair_queueing_size( r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fq = FQ(qdisc); - if (streq(lvalue, "Quantum")) + if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum")) p = &fq->quantum; - else if (streq(lvalue, "InitialQuantum")) + else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum")) p = &fq->initial_quantum; else assert_not_reached("Invalid lvalue"); @@ -214,13 +218,13 @@ int config_parse_fair_queueing_size( r = parse_size(rvalue, 1024, &sz); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } if (sz > UINT32_MAX) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified '%s=' is too large, ignoring assignment: %s", lvalue, rvalue); return 0; @@ -257,9 +261,11 @@ int config_parse_fair_queueing_bool( r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fq = FQ(qdisc); @@ -272,7 +278,7 @@ int config_parse_fair_queueing_bool( r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -310,9 +316,11 @@ int config_parse_fair_queueing_usec( r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fq = FQ(qdisc); @@ -325,13 +333,13 @@ int config_parse_fair_queueing_usec( r = parse_sec(rvalue, &sec); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } if (sec > UINT32_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified '%s=' is too large, ignoring assignment: %s", lvalue, rvalue); return 0; @@ -369,9 +377,11 @@ int config_parse_fair_queueing_max_rate( r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } fq = FQ(qdisc); @@ -384,13 +394,13 @@ int config_parse_fair_queueing_max_rate( r = parse_size(rvalue, 1000, &sz); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } if (sz / 8 > UINT32_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified '%s=' is too large, ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/gred.c b/src/network/tc/gred.c new file mode 100644 index 000000000..5629ecd01 --- /dev/null +++ b/src/network/tc/gred.c @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "string-util.h" + +static int generic_random_early_detection_init(QDisc *qdisc) { + GenericRandomEarlyDetection *gred; + + assert(qdisc); + + gred = GRED(qdisc); + + gred->grio = -1; + + return 0; +} + +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); + + opt.DPs = gred->virtual_queues; + opt.def_DP = gred->default_virtual_queue; + + if (gred->grio >= 0) + opt.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"); + + r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_GRED_DPS attribute: %m"); + + 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 0; +} + +static int generic_random_early_detection_verify(QDisc *qdisc) { + GenericRandomEarlyDetection *gred = GRED(qdisc); + + if (gred->default_virtual_queue >= gred->virtual_queues) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: DefaultVirtualQueue= must be less than VirtualQueues=. " + "Ignoring [GenericRandomEarlyDetection] section from line %u.", + qdisc->section->filename, qdisc->section->line); + + return 0; +} + +int config_parse_generic_random_early_detection_u32( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + GenericRandomEarlyDetection *gred; + Network *network = data; + uint32_t *p; + uint32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + gred = GRED(qdisc); + + if (streq(lvalue, "VirtualQueues")) + p = &gred->virtual_queues; + else if (streq(lvalue, "DefaultVirtualQueue")) + p = &gred->default_virtual_queue; + else + assert_not_reached("Invalid lvalue."); + + if (isempty(rvalue)) { + *p = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (v > MAX_DPs) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + } + + *p = v; + qdisc = NULL; + + return 0; +} +int config_parse_generic_random_early_detection_bool( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + GenericRandomEarlyDetection *gred; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + gred = GRED(qdisc); + + if (isempty(rvalue)) { + gred->grio = -1; + + qdisc = NULL; + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + gred->grio = r; + qdisc = NULL; + + return 0; +} + +const QDiscVTable gred_vtable = { + .object_size = sizeof(GenericRandomEarlyDetection), + .tca_kind = "gred", + .init = generic_random_early_detection_init, + .fill_message = generic_random_early_detection_fill_message, + .verify = generic_random_early_detection_verify, +}; diff --git a/src/network/tc/gred.h b/src/network/tc/gred.h new file mode 100644 index 000000000..4fb2b37c1 --- /dev/null +++ b/src/network/tc/gred.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct GenericRandomEarlyDetection { + QDisc meta; + + uint32_t virtual_queues; + uint32_t default_virtual_queue; + int grio; +} GenericRandomEarlyDetection; + +DEFINE_QDISC_CAST(GRED, GenericRandomEarlyDetection); +extern const QDiscVTable gred_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_u32); +CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_bool); diff --git a/src/network/tc/hhf.c b/src/network/tc/hhf.c new file mode 100644 index 000000000..324a975aa --- /dev/null +++ b/src/network/tc/hhf.c @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "hhf.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" +#include "util.h" + +static int heavy_hitter_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + HeavyHitterFilter *hhf; + int r; + + assert(link); + assert(qdisc); + assert(req); + + 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"); + + 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"); + } + + 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 0; +} + +int config_parse_heavy_hitter_filter_packet_limit( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + HeavyHitterFilter *hhf; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_HHF, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + hhf = HHF(qdisc); + + if (isempty(rvalue)) { + hhf->packet_limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &hhf->packet_limit); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +const QDiscVTable hhf_vtable = { + .object_size = sizeof(HeavyHitterFilter), + .tca_kind = "hhf", + .fill_message = heavy_hitter_filter_fill_message, +}; diff --git a/src/network/tc/hhf.h b/src/network/tc/hhf.h new file mode 100644 index 000000000..a555998ee --- /dev/null +++ b/src/network/tc/hhf.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct HeavyHitterFilter { + QDisc meta; + + uint32_t packet_limit; +} HeavyHitterFilter; + +DEFINE_QDISC_CAST(HHF, HeavyHitterFilter); +extern const QDiscVTable hhf_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_heavy_hitter_filter_packet_limit); diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c new file mode 100644 index 000000000..65481a76b --- /dev/null +++ b/src/network/tc/htb.c @@ -0,0 +1,489 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "htb.h" +#include "string-util.h" +#include "tc-util.h" + +#define HTB_DEFAULT_RATE_TO_QUANTUM 10 +#define HTB_DEFAULT_MTU 1600 /* Ethernet packet length */ + +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); + + opt.rate2quantum = htb->rate_to_quantum; + opt.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"); + + 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"); + + 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 0; +} + +int config_parse_hierarchy_token_bucket_default_class( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + HierarchyTokenBucket *htb; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + htb = HTB(qdisc); + + if (isempty(rvalue)) { + htb->default_class = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32_full(rvalue, 16, &htb->default_class); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +int config_parse_hierarchy_token_bucket_u32( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + HierarchyTokenBucket *htb; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + htb = HTB(qdisc); + + if (isempty(rvalue)) { + htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &htb->rate_to_quantum); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +static int hierarchy_token_bucket_init(QDisc *qdisc) { + HierarchyTokenBucket *htb; + + assert(qdisc); + + htb = HTB(qdisc); + + htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM; + + return 0; +} + +const QDiscVTable htb_vtable = { + .object_size = sizeof(HierarchyTokenBucket), + .tca_kind = "htb", + .fill_message = hierarchy_token_bucket_fill_message, + .init = hierarchy_token_bucket_init, +}; + +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; + + assert(link); + assert(tclass); + assert(req); + + 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; + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + } + + 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"); + } + + 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"); + + 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"); + + 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 0; +} + +int config_parse_hierarchy_token_bucket_class_u32( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + HierarchyTokenBucketClass *htb; + Network *network = data; + uint32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + htb = TCLASS_TO_HTB(tclass); + + if (isempty(rvalue)) { + htb->priority = 0; + tclass = NULL; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + htb->priority = v; + tclass = NULL; + + return 0; +} + +int config_parse_hierarchy_token_bucket_class_size( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + HierarchyTokenBucketClass *htb; + Network *network = data; + uint64_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + htb = TCLASS_TO_HTB(tclass); + + if (isempty(rvalue)) { + if (streq(lvalue, "QuantumBytes")) + htb->quantum = 0; + else if (streq(lvalue, "MTUBytes")) + htb->mtu = HTB_DEFAULT_MTU; + else if (streq(lvalue, "OverheadBytes")) + htb->overhead = 0; + else if (streq(lvalue, "BufferBytes")) + htb->buffer = 0; + else if (streq(lvalue, "CeilBufferBytes")) + htb->ceil_buffer = 0; + else + assert_not_reached("Invalid lvalue"); + + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1024, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if ((streq(lvalue, "OverheadBytes") && v > UINT16_MAX) || v > UINT32_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "QuantumBytes")) + htb->quantum = v; + else if (streq(lvalue, "OverheadBytes")) + htb->overhead = v; + else if (streq(lvalue, "MTUBytes")) + htb->mtu = v; + else if (streq(lvalue, "BufferBytes")) + htb->buffer = v; + else if (streq(lvalue, "CeilBufferBytes")) + htb->ceil_buffer = v; + else + assert_not_reached("Invalid lvalue"); + + tclass = NULL; + + return 0; +} + +int config_parse_hierarchy_token_bucket_class_rate( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + HierarchyTokenBucketClass *htb; + Network *network = data; + uint64_t *v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + htb = TCLASS_TO_HTB(tclass); + if (streq(lvalue, "Rate")) + v = &htb->rate; + else if (streq(lvalue, "CeilRate")) + v = &htb->ceil_rate; + else + assert_not_reached("Invalid lvalue"); + + if (isempty(rvalue)) { + *v = 0; + + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + *v /= 8; + tclass = NULL; + + return 0; +} + +static int hierarchy_token_bucket_class_init(TClass *tclass) { + HierarchyTokenBucketClass *htb; + + assert(tclass); + + htb = TCLASS_TO_HTB(tclass); + + htb->mtu = HTB_DEFAULT_MTU; + + return 0; +} + +static int hierarchy_token_bucket_class_verify(TClass *tclass) { + HierarchyTokenBucketClass *htb; + uint32_t hz; + int r; + + assert(tclass); + + htb = TCLASS_TO_HTB(tclass); + + if (htb->rate == 0) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Rate= is mandatory. " + "Ignoring [HierarchyTokenBucketClass] section from line %u.", + tclass->section->filename, tclass->section->line); + + /* if CeilRate= setting is missing, use the same as Rate= */ + if (htb->ceil_rate == 0) + htb->ceil_rate = htb->rate; + + r = tc_init(NULL, &hz); + if (r < 0) + return log_error_errno(r, "Failed to read /proc/net/psched: %m"); + + if (htb->buffer == 0) + htb->buffer = htb->rate / hz + htb->mtu; + if (htb->ceil_buffer == 0) + htb->ceil_buffer = htb->ceil_rate / hz + htb->mtu; + + return 0; +} + +const TClassVTable htb_tclass_vtable = { + .object_size = sizeof(HierarchyTokenBucketClass), + .tca_kind = "htb", + .fill_message = hierarchy_token_bucket_class_fill_message, + .init = hierarchy_token_bucket_class_init, + .verify = hierarchy_token_bucket_class_verify, +}; diff --git a/src/network/tc/htb.h b/src/network/tc/htb.h new file mode 100644 index 000000000..b385872e0 --- /dev/null +++ b/src/network/tc/htb.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" +#include "tclass.h" + +typedef struct HierarchyTokenBucket { + QDisc meta; + + uint32_t default_class; + uint32_t rate_to_quantum; +} HierarchyTokenBucket; + +DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket); +extern const QDiscVTable htb_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class); +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32); + +typedef struct HierarchyTokenBucketClass { + TClass meta; + + uint32_t priority; + uint32_t quantum; + uint32_t mtu; + uint16_t overhead; + uint64_t rate; + uint32_t buffer; + uint64_t ceil_rate; + uint32_t ceil_buffer; +} HierarchyTokenBucketClass; + +DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass); +extern const TClassVTable htb_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_u32); +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_size); +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_rate); diff --git a/src/network/tc/netem.c b/src/network/tc/netem.c index 7d0add9e3..a94a9a369 100644 --- a/src/network/tc/netem.c +++ b/src/network/tc/netem.c @@ -80,9 +80,11 @@ int config_parse_network_emulator_delay( r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } ne = NETEM(qdisc); @@ -98,7 +100,7 @@ int config_parse_network_emulator_delay( r = parse_sec(rvalue, &u); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -140,9 +142,11 @@ int config_parse_network_emulator_rate( r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } ne = NETEM(qdisc); @@ -158,7 +162,7 @@ int config_parse_network_emulator_rate( r = parse_tc_percent(rvalue, &rate); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; @@ -198,9 +202,11 @@ int config_parse_network_emulator_packet_limit( r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } ne = NETEM(qdisc); @@ -213,7 +219,7 @@ int config_parse_network_emulator_packet_limit( r = safe_atou(rvalue, &ne->limit); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/pie.c b/src/network/tc/pie.c new file mode 100644 index 000000000..eccbaa2cf --- /dev/null +++ b/src/network/tc/pie.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "pie.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" + +static int pie_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + ProportionalIntegralControllerEnhanced *pie; + int r; + + assert(link); + assert(qdisc); + assert(req); + + 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"); + + 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"); + } + + 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 0; +} + +int config_parse_pie_packet_limit( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + ProportionalIntegralControllerEnhanced *pie; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_PIE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + pie = PIE(qdisc); + + if (isempty(rvalue)) { + pie->packet_limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &pie->packet_limit); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +const QDiscVTable pie_vtable = { + .object_size = sizeof(ProportionalIntegralControllerEnhanced), + .tca_kind = "pie", + .fill_message = pie_fill_message, +}; diff --git a/src/network/tc/pie.h b/src/network/tc/pie.h new file mode 100644 index 000000000..7c764742a --- /dev/null +++ b/src/network/tc/pie.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct ProportionalIntegralControllerEnhanced { + QDisc meta; + + uint32_t packet_limit; +} ProportionalIntegralControllerEnhanced; + +DEFINE_QDISC_CAST(PIE, ProportionalIntegralControllerEnhanced); +extern const QDiscVTable pie_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_pie_packet_limit); diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 0619e894c..e1262c128 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -12,19 +12,34 @@ #include "qdisc.h" #include "set.h" #include "string-util.h" +#include "strv.h" +#include "tc-util.h" const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = { + [QDISC_KIND_BFIFO] = &bfifo_vtable, + [QDISC_KIND_CAKE] = &cake_vtable, [QDISC_KIND_CODEL] = &codel_vtable, + [QDISC_KIND_DRR] = &drr_vtable, + [QDISC_KIND_ETS] = &ets_vtable, [QDISC_KIND_FQ] = &fq_vtable, [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable, + [QDISC_KIND_GRED] = &gred_vtable, + [QDISC_KIND_HHF] = &hhf_vtable, + [QDISC_KIND_HTB] = &htb_vtable, [QDISC_KIND_NETEM] = &netem_vtable, + [QDISC_KIND_PIE] = &pie_vtable, + [QDISC_KIND_QFQ] = &qfq_vtable, + [QDISC_KIND_PFIFO] = &pfifo_vtable, + [QDISC_KIND_PFIFO_FAST] = &pfifo_fast_vtable, + [QDISC_KIND_PFIFO_HEAD_DROP] = &pfifo_head_drop_vtable, + [QDISC_KIND_SFB] = &sfb_vtable, [QDISC_KIND_SFQ] = &sfq_vtable, [QDISC_KIND_TBF] = &tbf_vtable, [QDISC_KIND_TEQL] = &teql_vtable, }; static int qdisc_new(QDiscKind kind, QDisc **ret) { - QDisc *qdisc; + _cleanup_(qdisc_freep) QDisc *qdisc = NULL; int r; if (kind == _QDISC_KIND_INVALID) { @@ -33,6 +48,7 @@ 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, @@ -42,6 +58,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { if (!qdisc) return -ENOMEM; + qdisc->meta.kind = TC_KIND_QDISC, qdisc->family = AF_UNSPEC; qdisc->parent = TC_H_ROOT; qdisc->kind = kind; @@ -61,7 +78,8 @@ 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_(qdisc_freep) QDisc *qdisc = NULL; - QDisc *existing; + TrafficControl *existing; + QDisc *q = NULL; int r; assert(network); @@ -73,15 +91,20 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns if (r < 0) return r; - existing = ordered_hashmap_get(network->qdiscs_by_section, n); + existing = ordered_hashmap_get(network->tc_by_section, n); if (existing) { - if (existing->kind != _QDISC_KIND_INVALID && - kind != _QDISC_KIND_INVALID && - existing->kind != kind) + if (existing->kind != TC_KIND_QDISC) return -EINVAL; - if (existing->kind == kind || kind == _QDISC_KIND_INVALID) { - *ret = existing; + q = TC_TO_QDISC(existing); + + if (q->kind != _QDISC_KIND_INVALID && + kind != _QDISC_KIND_INVALID && + q->kind != kind) + return -EINVAL; + + if (q->kind == kind || kind == _QDISC_KIND_INVALID) { + *ret = q; return 0; } } @@ -90,23 +113,23 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns if (r < 0) return r; - if (existing) { - qdisc->family = existing->family; - qdisc->handle = existing->handle; - qdisc->parent = existing->parent; - qdisc->tca_kind = TAKE_PTR(existing->tca_kind); + if (q) { + qdisc->family = q->family; + qdisc->handle = q->handle; + qdisc->parent = q->parent; + qdisc->tca_kind = TAKE_PTR(q->tca_kind); - qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n)); + qdisc_free(q); } qdisc->network = network; qdisc->section = TAKE_PTR(n); - r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops); + r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops); if (r < 0) return r; - r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc); + r = ordered_hashmap_put(network->tc_by_section, qdisc->section, TC(qdisc)); if (r < 0) return r; @@ -119,7 +142,7 @@ void qdisc_free(QDisc *qdisc) { return; if (qdisc->network && qdisc->section) - ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section); + ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section); network_config_section_free(qdisc->section); @@ -131,8 +154,8 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); - assert(link->qdisc_messages > 0); - link->qdisc_messages--; + assert(link->tc_messages > 0); + link->tc_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -144,9 +167,9 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } - if (link->qdisc_messages == 0) { - log_link_debug(link, "QDisc configured"); - link->qdiscs_configured = true; + if (link->tc_messages == 0) { + log_link_debug(link, "Traffic control configured"); + link->tc_configured = true; link_check_ready(link); } @@ -203,7 +226,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) { return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); - link->qdisc_messages++; + link->tc_messages++; return 0; } @@ -265,8 +288,13 @@ int config_parse_qdisc_parent( assert(data); r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } if (streq(rvalue, "root")) { qdisc->parent = TC_H_ROOT; @@ -279,19 +307,21 @@ int config_parse_qdisc_parent( qdisc->parent = TC_H_INGRESS; qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse 'Parent=', ignoring assignment: %s", - rvalue); - return 0; + r = parse_handle(rvalue, &qdisc->parent); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse 'Parent=', ignoring assignment: %s", + rvalue); + return 0; + } } - if (streq(rvalue, "root")) - qdisc->tca_kind = mfree(qdisc->tca_kind); - else { + if (STR_IN_SET(rvalue, "clsact", "ingress")) { r = free_and_strdup(&qdisc->tca_kind, rvalue); if (r < 0) return log_oom(); - } + } else + qdisc->tca_kind = mfree(qdisc->tca_kind); qdisc = NULL; @@ -321,8 +351,13 @@ int config_parse_qdisc_handle( assert(data); r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); - if (r < 0) - return r; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } if (isempty(rvalue)) { qdisc->handle = TC_H_UNSPEC; @@ -332,7 +367,7 @@ int config_parse_qdisc_handle( r = safe_atou16_full(rvalue, 16, &n); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 'Handle=', ignoring assignment: %s", rvalue); return 0; diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 8e4a70de5..0c9c0544b 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -6,12 +6,26 @@ #include "networkd-link.h" #include "networkd-network.h" #include "networkd-util.h" +#include "tc.h" typedef enum QDiscKind { + QDISC_KIND_BFIFO, + QDISC_KIND_CAKE, QDISC_KIND_CODEL, + QDISC_KIND_DRR, + QDISC_KIND_ETS, QDISC_KIND_FQ, QDISC_KIND_FQ_CODEL, + QDISC_KIND_GRED, + QDISC_KIND_HHF, + QDISC_KIND_HTB, QDISC_KIND_NETEM, + QDISC_KIND_PFIFO, + QDISC_KIND_PFIFO_FAST, + QDISC_KIND_PFIFO_HEAD_DROP, + QDISC_KIND_PIE, + QDISC_KIND_QFQ, + QDISC_KIND_SFB, QDISC_KIND_SFQ, QDISC_KIND_TBF, QDISC_KIND_TEQL, @@ -20,6 +34,8 @@ typedef enum QDiscKind { } QDiscKind; typedef struct QDisc { + TrafficControl meta; + NetworkConfigSection *section; Network *network; @@ -65,13 +81,25 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free); +DEFINE_TC_CAST(QDISC, QDisc); + CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent); CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle); +#include "cake.h" #include "codel.h" +#include "ets.h" +#include "fifo.h" #include "fq-codel.h" #include "fq.h" +#include "gred.h" +#include "hhf.h" +#include "htb.h" +#include "pie.h" +#include "qfq.h" #include "netem.h" +#include "drr.h" +#include "sfb.h" #include "sfq.h" #include "tbf.h" #include "teql.h" diff --git a/src/network/tc/qfq.c b/src/network/tc/qfq.c new file mode 100644 index 000000000..2104067f3 --- /dev/null +++ b/src/network/tc/qfq.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "parse-util.h" +#include "qdisc.h" +#include "qfq.h" +#include "string-util.h" + +#define QFQ_MAX_WEIGHT (1 << 10) +#define QFQ_MIN_MAX_PACKET 512 +#define QFQ_MAX_MAX_PACKET (1 << 16) + +const QDiscVTable qfq_vtable = { + .object_size = sizeof(QuickFairQueueing), + .tca_kind = "qfq", +}; + +static int quick_fair_queueing_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { + QuickFairQueueingClass *qfq; + int r; + + assert(link); + assert(tclass); + assert(req); + + 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"); + + 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"); + } + + 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"); + } + + 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 0; +} + +int config_parse_quick_fair_queueing_weight( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + QuickFairQueueingClass *qfq; + Network *network = data; + uint32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + qfq = TCLASS_TO_QFQ(tclass); + + if (isempty(rvalue)) { + qfq->weight = 0; + tclass = NULL; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (v == 0 || v > QFQ_MAX_WEIGHT) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qfq->weight = v; + tclass = NULL; + + return 0; +} + +int config_parse_quick_fair_queueing_max_packet( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + QuickFairQueueingClass *qfq; + Network *network = data; + uint64_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + qfq = TCLASS_TO_QFQ(tclass); + + if (isempty(rvalue)) { + qfq->max_packet = 0; + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1024, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (v < QFQ_MIN_MAX_PACKET || v > QFQ_MAX_MAX_PACKET) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qfq->max_packet = (uint32_t) v; + tclass = NULL; + + return 0; +} + +const TClassVTable qfq_tclass_vtable = { + .object_size = sizeof(QuickFairQueueingClass), + .tca_kind = "qfq", + .fill_message = quick_fair_queueing_class_fill_message, +}; diff --git a/src/network/tc/qfq.h b/src/network/tc/qfq.h new file mode 100644 index 000000000..10bab3e64 --- /dev/null +++ b/src/network/tc/qfq.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct QuickFairQueueing { + QDisc meta; +} QuickFairQueueing; + +DEFINE_QDISC_CAST(QFQ, QuickFairQueueing); +extern const QDiscVTable qfq_vtable; + +typedef struct QuickFairQueueingClass { + TClass meta; + + uint32_t weight; + uint32_t max_packet; +} QuickFairQueueingClass; + +DEFINE_TCLASS_CAST(QFQ, QuickFairQueueingClass); +extern const TClassVTable qfq_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_weight); +CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_max_packet); diff --git a/src/network/tc/sfb.c b/src/network/tc/sfb.c new file mode 100644 index 000000000..3692a50ed --- /dev/null +++ b/src/network/tc/sfb.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "sfb.h" +#include "string-util.h" + +static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + StochasticFairBlue *sfb; + struct tc_sfb_qopt opt = { + .rehash_interval = 600*1000, + .warmup_time = 60*1000, + .penalty_rate = 10, + .penalty_burst = 20, + .increment = (SFB_MAX_PROB + 1000) / 2000, + .decrement = (SFB_MAX_PROB + 10000) / 20000, + .max = 25, + .bin_size = 20, + }; + 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"); + + r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(struct tc_sfb_qopt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_SFB_PARMS attribute: %m"); + + 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 0; +} + +int config_parse_stochastic_fair_blue_u32( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + StochasticFairBlue *sfb; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_SFB, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + sfb = SFB(qdisc); + + if (isempty(rvalue)) { + sfb->packet_limit = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou32(rvalue, &sfb->packet_limit); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc = NULL; + + return 0; +} + +const QDiscVTable sfb_vtable = { + .object_size = sizeof(StochasticFairBlue), + .tca_kind = "sfb", + .fill_message = stochastic_fair_blue_fill_message, +}; diff --git a/src/network/tc/sfb.h b/src/network/tc/sfb.h new file mode 100644 index 000000000..3cc87d737 --- /dev/null +++ b/src/network/tc/sfb.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2020 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct StochasticFairBlue { + QDisc meta; + + uint32_t packet_limit; +} StochasticFairBlue; + +DEFINE_QDISC_CAST(SFB, StochasticFairBlue); +extern const QDiscVTable sfb_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fair_blue_u32); diff --git a/src/network/tc/sfq.c b/src/network/tc/sfq.c index ee66409b9..d671281c4 100644 --- a/src/network/tc/sfq.c +++ b/src/network/tc/sfq.c @@ -56,9 +56,11 @@ int config_parse_stochastic_fairness_queueing_perturb_period( r = qdisc_new_static(QDISC_KIND_SFQ, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } sfq = SFQ(qdisc); @@ -71,7 +73,7 @@ int config_parse_stochastic_fairness_queueing_perturb_period( r = parse_sec(rvalue, &sfq->perturb_period); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c index 0682ab4cc..cb3c3bcc1 100644 --- a/src/network/tc/tbf.c +++ b/src/network/tc/tbf.c @@ -12,8 +12,8 @@ #include "parse-util.h" #include "qdisc.h" #include "string-util.h" +#include "strv.h" #include "tc-util.h" -#include "util.h" static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { uint32_t rtab[256], ptab[256]; @@ -136,25 +136,96 @@ int config_parse_token_bucket_filter_size( r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } tbf = TBF(qdisc); if (isempty(rvalue)) { - if (streq(lvalue, "Rate")) - tbf->rate = 0; - else if (streq(lvalue, "Burst")) + if (STR_IN_SET(lvalue, "BurstBytes", "Burst")) tbf->burst = 0; - else if (streq(lvalue, "LimitSize")) + else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize")) tbf->limit = 0; else if (streq(lvalue, "MTUBytes")) tbf->mtu = 0; else if (streq(lvalue, "MPUBytes")) tbf->mpu = 0; - else if (streq(lvalue, "PeakRate")) - tbf->peak_rate = 0; + else + assert_not_reached("unknown lvalue"); + + qdisc = NULL; + return 0; + } + + r = parse_size(rvalue, 1024, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (STR_IN_SET(lvalue, "BurstBytes", "Burst")) + tbf->burst = k; + else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize")) + tbf->limit = k; + else if (streq(lvalue, "MPUBytes")) + tbf->mpu = k; + else if (streq(lvalue, "MTUBytes")) + tbf->mtu = k; + else + assert_not_reached("unknown lvalue"); + + qdisc = NULL; + + return 0; +} + +int config_parse_token_bucket_filter_rate( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + Network *network = data; + TokenBucketFilter *tbf; + uint64_t k, *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + tbf = TBF(qdisc); + if (streq(lvalue, "Rate")) + p = &tbf->rate; + else if (streq(lvalue, "PeakRate")) + p = &tbf->peak_rate; + else + assert_not_reached("unknown lvalue"); + + if (isempty(rvalue)) { + *p = 0; qdisc = NULL; return 0; @@ -162,24 +233,13 @@ int config_parse_token_bucket_filter_size( r = parse_size(rvalue, 1000, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } - if (streq(lvalue, "Rate")) - tbf->rate = k / 8; - else if (streq(lvalue, "Burst")) - tbf->burst = k; - else if (streq(lvalue, "LimitSize")) - tbf->limit = k; - else if (streq(lvalue, "MPUBytes")) - tbf->mpu = k; - else if (streq(lvalue, "MTUBytes")) - tbf->mtu = k; - else if (streq(lvalue, "PeakRate")) - tbf->peak_rate = k / 8; + *p = k / 8; qdisc = NULL; @@ -212,9 +272,11 @@ int config_parse_token_bucket_filter_latency( r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } tbf = TBF(qdisc); @@ -227,7 +289,7 @@ int config_parse_token_bucket_filter_latency( r = parse_sec(rvalue, &u); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; diff --git a/src/network/tc/tbf.h b/src/network/tc/tbf.h index b66aef206..a785be25e 100644 --- a/src/network/tc/tbf.h +++ b/src/network/tc/tbf.h @@ -23,3 +23,4 @@ extern const QDiscVTable tbf_vtable; CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_latency); CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_size); +CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_rate); diff --git a/src/network/tc/tc-util.c b/src/network/tc/tc-util.c index c46550f95..8a5afeab2 100644 --- a/src/network/tc/tc-util.c +++ b/src/network/tc/tc-util.c @@ -2,43 +2,52 @@ * Copyright © 2019 VMware, Inc. */ #include "alloc-util.h" +#include "extract-word.h" #include "fileio.h" #include "parse-util.h" #include "tc-util.h" #include "time-util.h" -static int tc_init(double *ticks_in_usec) { - uint32_t clock_resolution, ticks_to_usec, usec_to_ticks; - _cleanup_free_ char *line = NULL; - double clock_factor; - int r; +int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz) { + static double ticks_in_usec = -1; + static uint32_t hz; - r = read_one_line_file("/proc/net/psched", &line); - if (r < 0) - return r; + if (ticks_in_usec < 0) { + uint32_t clock_resolution, ticks_to_usec, usec_to_ticks; + _cleanup_free_ char *line = NULL; + double clock_factor; + int r; - r = sscanf(line, "%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution); - if (r < 3) - return -EIO; + r = read_one_line_file("/proc/net/psched", &line); + if (r < 0) + return r; - clock_factor = (double) clock_resolution / USEC_PER_SEC; - *ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor; + r = sscanf(line, "%08x%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution, &hz); + if (r < 4) + return -EIO; + + clock_factor = (double) clock_resolution / USEC_PER_SEC; + ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor; + } + + if (ret_ticks_in_usec) + *ret_ticks_in_usec = ticks_in_usec; + if (ret_hz) + *ret_hz = hz; return 0; } int tc_time_to_tick(usec_t t, uint32_t *ret) { - static double ticks_in_usec = -1; + double ticks_in_usec; usec_t a; int r; assert(ret); - if (ticks_in_usec < 0) { - r = tc_init(&ticks_in_usec); - if (r < 0) - return r; - } + r = tc_init(&ticks_in_usec, NULL); + if (r < 0) + return r; a = t * ticks_in_usec; if (a > UINT32_MAX) @@ -48,7 +57,7 @@ int tc_time_to_tick(usec_t t, uint32_t *ret) { return 0; } -int parse_tc_percent(const char *s, uint32_t *percent) { +int parse_tc_percent(const char *s, uint32_t *percent) { int r; assert(s); @@ -92,3 +101,32 @@ int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_ rate->linklayer = TC_LINKLAYER_ETHERNET; return 0; } + +int parse_handle(const char *t, uint32_t *ret) { + _cleanup_free_ char *word = NULL; + uint16_t major, minor; + int r; + + assert(t); + assert(ret); + + /* Extract the major number. */ + r = extract_first_word(&t, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + if (!t) + return -EINVAL; + + r = safe_atou16_full(word, 16, &major); + if (r < 0) + return r; + + r = safe_atou16_full(t, 16, &minor); + if (r < 0) + return r; + + *ret = ((uint32_t) major << 16) | minor; + return 0; +} diff --git a/src/network/tc/tc-util.h b/src/network/tc/tc-util.h index c901f5069..6287b35a7 100644 --- a/src/network/tc/tc-util.h +++ b/src/network/tc/tc-util.h @@ -6,7 +6,9 @@ #include "time-util.h" +int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz); int tc_time_to_tick(usec_t t, uint32_t *ret); int parse_tc_percent(const char *s, uint32_t *percent); int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret); int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu); +int parse_handle(const char *t, uint32_t *ret); diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c new file mode 100644 index 000000000..30a00133d --- /dev/null +++ b/src/network/tc/tc.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "macro.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("Invalid traffic control type"); + } +} + +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("Invalid traffic control type"); + } +} + +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("Invalid traffic control type"); + } +} diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h new file mode 100644 index 000000000..defa0b774 --- /dev/null +++ b/src/network/tc/tc.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "networkd-link.h" + +typedef enum TrafficControlKind { + TC_KIND_QDISC, + TC_KIND_TCLASS, + TC_KIND_FILTER, + _TC_KIND_MAX, + _TC_KIND_INVALID = -1, +} 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 traffic_control_configure(Link *link, TrafficControl *tc); +int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c new file mode 100644 index 000000000..9a39713b3 --- /dev/null +++ b/src/network/tc/tclass.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2019 VMware, Inc. */ + +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "in-addr-util.h" +#include "netlink-util.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" +#include "tc-util.h" +#include "tclass.h" + +const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = { + [TCLASS_KIND_DRR] = &drr_tclass_vtable, + [TCLASS_KIND_HTB] = &htb_tclass_vtable, + [TCLASS_KIND_QFQ] = &qfq_tclass_vtable, +}; + +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; + + tclass->meta.kind = TC_KIND_TCLASS, + 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); + + return 0; +} + +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_(tclass_freep) TClass *tclass = NULL; + TrafficControl *existing; + 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->tc_by_section, n); + if (existing) { + TClass *t; + + if (existing->kind != TC_KIND_TCLASS) + return -EINVAL; + + t = TC_TO_TCLASS(existing); + + if (t->kind != kind) + return -EINVAL; + + *ret = t; + return 0; + } + + r = tclass_new(kind, &tclass); + if (r < 0) + return r; + + tclass->network = network; + tclass->section = TAKE_PTR(n); + + r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass); + if (r < 0) + return r; + + *ret = TAKE_PTR(tclass); + return 0; +} + +void tclass_free(TClass *tclass) { + if (!tclass) + return; + + if (tclass->network && tclass->section) + ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section); + + network_config_section_free(tclass->section); + + free(tclass); +} + +static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(link); + assert(link->tc_messages > 0); + link->tc_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_message_error_errno(link, m, r, "Could not set TClass"); + link_enter_failed(link); + return 1; + } + + if (link->tc_messages == 0) { + log_link_debug(link, "Traffic control configured"); + link->tc_configured = true; + link_check_ready(link); + } + + return 1; +} + +int tclass_configure(Link *link, TClass *tclass) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + + r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m"); + + r = sd_rtnl_message_set_tclass_parent(req, tclass->parent); + 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"); + + if (TCLASS_VTABLE(tclass)->fill_message) { + r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req); + 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; +} + +int tclass_section_verify(TClass *tclass) { + int r; + + assert(tclass); + + if (section_is_invalid(tclass->section)) + return -EINVAL; + + if (TCLASS_VTABLE(tclass)->verify) { + r = TCLASS_VTABLE(tclass)->verify(tclass); + if (r < 0) + return r; + } + + return 0; +} + +int config_parse_tclass_parent( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(ltype, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + if (streq(rvalue, "root")) + tclass->parent = TC_H_ROOT; + else { + r = parse_handle(rvalue, &tclass->parent); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse 'Parent=', ignoring assignment: %s", + rvalue); + return 0; + } + } + + tclass = NULL; + + return 0; +} + +int config_parse_tclass_classid( + 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(ltype, network, filename, section_line, &tclass); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + return 0; + } + + if (isempty(rvalue)) { + tclass->classid = TC_H_UNSPEC; + tclass = NULL; + return 0; + } + + r = parse_handle(rvalue, &tclass->classid); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse 'ClassId=', ignoring assignment: %s", + rvalue); + return 0; + } + + tclass = NULL; + + return 0; +} diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h new file mode 100644 index 000000000..dc6886ac3 --- /dev/null +++ b/src/network/tc/tclass.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2019 VMware, Inc. */ +#pragma once + +#include "conf-parser.h" +#include "networkd-link.h" +#include "networkd-network.h" +#include "networkd-util.h" +#include "tc.h" + +typedef enum TClassKind { + TCLASS_KIND_DRR, + TCLASS_KIND_HTB, + TCLASS_KIND_QFQ, + _TCLASS_KIND_MAX, + _TCLASS_KIND_INVALID = -1, +} TClassKind; + +typedef struct TClass { + TrafficControl meta; + + NetworkConfigSection *section; + Network *network; + + uint32_t classid; + uint32_t parent; + + TClassKind kind; +} TClass; + +typedef struct TClassVTable { + size_t object_size; + const char *tca_kind; + /* called in tclass_new() */ + int (*init)(TClass *tclass); + int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m); + int (*verify)(TClass *tclass); +} TClassVTable; + +extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX]; + +#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL) + +/* For casting a tclass into the various tclass kinds */ +#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase) \ + static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) { \ + if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \ + return NULL; \ + \ + return (MixedCase*) t; \ + } + +/* For casting the various tclass kinds into a tclass */ +#define TCLASS(t) (&(t)->meta) + +void 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); + +DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free); + +DEFINE_TC_CAST(TCLASS, TClass); + +CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent); +CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid); + +#include "drr.h" +#include "htb.h" +#include "qfq.h" diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c index aac949961..bc64860a3 100644 --- a/src/network/tc/teql.c +++ b/src/network/tc/teql.c @@ -57,9 +57,11 @@ int config_parse_trivial_link_equalizer_id( r = qdisc_new_static(QDISC_KIND_TEQL, network, filename, section_line, &qdisc); if (r == -ENOMEM) return log_oom(); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, - "More than one kind of queueing discipline, ignoring assignment: %m"); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } teql = TEQL(qdisc); @@ -72,13 +74,13 @@ int config_parse_trivial_link_equalizer_id( r = safe_atou(rvalue, &id); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring assignment: %s", lvalue, rvalue); return 0; } if (id > INT_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "'%s=' is too large, ignoring assignment: %s", lvalue, rvalue); } diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c index d441099b5..d84d746c3 100644 --- a/src/network/test-routing-policy-rule.c +++ b/src/network/test-routing-policy-rule.c @@ -53,7 +53,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons log_info("$ %s", cmd); assert_se(system(cmd) == 0); - set_free_with_destructor(rules, routing_policy_rule_free); + set_free(rules); } int main(int argc, char **argv) { diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index 40a29f19a..6ab26d3ca 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -323,7 +323,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore, if (r < 0) return r; - (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); if (timeout > 0) { diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c index 17ed5d38c..cfd9093f1 100644 --- a/src/network/wait-online/wait-online.c +++ b/src/network/wait-online/wait-online.c @@ -183,8 +183,8 @@ static int parse_argv(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; int r; log_setup_service(); diff --git a/src/notify/notify.c b/src/notify/notify.c index a76f33769..69d473401 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -18,6 +18,7 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "time-util.h" #include "user-util.h" #include "util.h" @@ -27,6 +28,7 @@ static const char *arg_status = NULL; static bool arg_booted = false; static uid_t arg_uid = UID_INVALID; static gid_t arg_gid = GID_INVALID; +static bool arg_no_block = false; static int help(void) { _cleanup_free_ char *link = NULL; @@ -45,6 +47,7 @@ static int help(void) { " --uid=USER Set user to send from\n" " --status=TEXT Set status text\n" " --booted Check if the system was booted up with systemd\n" + " --no-block Do not wait until operation finished\n" "\nSee the %s for details.\n" , program_invocation_short_name , ansi_highlight(), ansi_normal() @@ -54,6 +57,26 @@ static int help(void) { return 0; } +static pid_t manager_pid(void) { + const char *e; + pid_t pid; + int r; + + /* If we run as a service managed by systemd --user the $MANAGERPID environment variable points to + * the service manager's PID. */ + e = getenv("MANAGERPID"); + if (!e) + return 0; + + r = parse_pid(e, &pid); + if (r < 0) { + log_warning_errno(r, "$MANAGERPID is set to an invalid PID, ignoring: %s", e); + return 0; + } + + return pid; +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -63,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_STATUS, ARG_BOOTED, ARG_UID, + ARG_NO_BLOCK }; static const struct option options[] = { @@ -73,6 +97,7 @@ static int parse_argv(int argc, char *argv[]) { { "status", required_argument, NULL, ARG_STATUS }, { "booted", no_argument, NULL, ARG_BOOTED }, { "uid", required_argument, NULL, ARG_UID }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, {} }; @@ -96,14 +121,25 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PID: - - if (optarg) { - if (parse_pid(optarg, &arg_pid) < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse PID %s.", optarg); - } else + if (isempty(optarg) || streq(optarg, "auto")) { arg_pid = getppid(); + if (arg_pid <= 1 || + arg_pid == manager_pid()) /* Don't send from PID 1 or the service + * manager's PID (which might be distinct from + * 1, if we are a --user instance), that'd just + * be confusing for the service manager */ + arg_pid = getpid(); + } else if (streq(optarg, "parent")) + arg_pid = getppid(); + else if (streq(optarg, "self")) + arg_pid = getpid(); + else { + r = parse_pid(optarg, &arg_pid); + if (r < 0) + return log_error_errno(r, "Failed to parse PID %s.", optarg); + } + break; case ARG_STATUS: @@ -126,6 +162,10 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_NO_BLOCK: + arg_no_block = true; + break; + case '?': return -EINVAL; @@ -151,6 +191,7 @@ static int run(int argc, char* argv[]) { _cleanup_strv_free_ char **final_env = NULL; char* our_env[4]; unsigned i = 0; + pid_t source_pid; int r; log_show_color(true); @@ -207,12 +248,33 @@ static int run(int argc, char* argv[]) { setreuid(arg_uid, (uid_t) -1) < 0) return log_error_errno(errno, "Failed to change UID: %m"); - r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n); + if (arg_pid > 0) + source_pid = arg_pid; + else { + /* Pretend the message originates from our parent, given that we are typically called from a + * shell script, i.e. we are not the main process of a service but only a child of it. */ + source_pid = getppid(); + if (source_pid <= 1 || + source_pid == manager_pid()) /* safety check: don't claim we'd send anything from PID 1 + * or the service manager itself */ + source_pid = 0; + } + r = sd_pid_notify(source_pid, false, n); if (r < 0) return log_error_errno(r, "Failed to notify init system: %m"); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "No status data could be sent: $NOTIFY_SOCKET was not set"); + + if (!arg_no_block) { + r = sd_notify_barrier(0, 5 * USEC_PER_SEC); + if (r < 0) + return log_error_errno(r, "Failed to invoke barrier: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "No status data could be sent: $NOTIFY_SOCKET was not set"); + } + return 0; } diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index f5048d947..a16ee5c60 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -199,16 +199,12 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) * namespace. */ static int get_process_controllers(Set **ret) { - _cleanup_set_free_free_ Set *controllers = NULL; + _cleanup_set_free_ Set *controllers = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(ret); - controllers = set_new(&string_hash_ops); - if (!controllers) - return -ENOMEM; - f = fopen("/proc/self/cgroup", "re"); if (!f) return errno == ENOENT ? -ESRCH : -errno; @@ -237,7 +233,7 @@ static int get_process_controllers(Set **ret) { if (STR_IN_SET(l, "", "name=systemd", "name=unified")) continue; - r = set_put_strdup(controllers, l); + r = set_put_strdup(&controllers, l); if (r < 0) return r; } @@ -303,7 +299,7 @@ static int mount_legacy_cgns_supported( uid_t uid_range, const char *selinux_apifs_context) { - _cleanup_set_free_free_ Set *controllers = NULL; + _cleanup_set_free_ Set *controllers = NULL; const char *cgroup_root = "/sys/fs/cgroup", *c; int r; @@ -323,7 +319,7 @@ static int mount_legacy_cgns_supported( * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply * pass uid 0 and not uid_shift to tmpfs_patch_options(). */ - r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options); + r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options); if (r < 0) return log_oom(); @@ -425,7 +421,7 @@ static int mount_legacy_cgns_unsupported( if (r == 0) { _cleanup_free_ char *options = NULL; - r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options); + r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options); if (r < 0) return log_oom(); diff --git a/src/nspawn/nspawn-def.h b/src/nspawn/nspawn-def.h index 9b54cda2f..ac3a1a02c 100644 --- a/src/nspawn/nspawn-def.h +++ b/src/nspawn/nspawn-def.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once #include diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c index 10e619273..69f36691f 100644 --- a/src/nspawn/nspawn-expose-ports.c +++ b/src/nspawn/nspawn-expose-ports.c @@ -52,7 +52,7 @@ int expose_port_parse(ExposePort **l, const char *s) { } if (r < 0) - return -EINVAL; + return r; LIST_FOREACH(ports, p, *l) if (p->protocol == protocol && p->host_port == host_port) @@ -62,9 +62,11 @@ int expose_port_parse(ExposePort **l, const char *s) { if (!p) return -ENOMEM; - p->protocol = protocol; - p->host_port = host_port; - p->container_port = container_port; + *p = (ExposePort) { + .protocol = protocol, + .host_port = host_port, + .container_port = container_port, + }; LIST_PREPEND(ports, *l, p); @@ -115,7 +117,6 @@ int expose_port_flush(ExposePort* l, union in_addr_union *exposed) { int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) { _cleanup_free_ struct local_address *addresses = NULL; - _cleanup_free_ char *pretty = NULL; union in_addr_union new_exposed; ExposePort *p; bool add; @@ -144,8 +145,11 @@ int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *ex if (in_addr_equal(af, exposed, &new_exposed)) return 0; - in_addr_to_string(af, &new_exposed, &pretty); - log_debug("New container IP is %s.", strna(pretty)); + if (DEBUG_LOGGING) { + _cleanup_free_ char *pretty = NULL; + in_addr_to_string(af, &new_exposed, &pretty); + log_debug("New container IP is %s.", strna(pretty)); + } LIST_FOREACH(ports, p, l) { diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 10c147a67..5599c6a1b 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -487,59 +487,6 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL); } -static int mkdir_userns(const char *path, mode_t mode, uid_t uid_shift) { - int r; - - assert(path); - - r = mkdir_errno_wrapper(path, mode); - if (r < 0 && r != -EEXIST) - return r; - - if (uid_shift == UID_INVALID) - return 0; - - if (lchown(path, uid_shift, uid_shift) < 0) - return -errno; - - return 0; -} - -static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, uid_t uid_shift) { - const char *p, *e; - int r; - - assert(path); - - if (prefix && !path_startswith(path, prefix)) - return -ENOTDIR; - - /* create every parent directory in the path, except the last component */ - p = path + strspn(path, "/"); - for (;;) { - char t[strlen(path) + 1]; - - e = p + strcspn(p, "/"); - p = e + strspn(e, "/"); - - /* Is this the last component? If so, then we're done */ - if (*p == 0) - break; - - memcpy(t, path, e - path); - t[e-path] = 0; - - if (prefix && path_startswith(prefix, t)) - continue; - - r = mkdir_userns(t, mode, uid_shift); - if (r < 0) - return r; - } - - return mkdir_userns(path, mode, uid_shift); -} - int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, @@ -598,29 +545,38 @@ int mount_all(const char *dest, PROC_READ_ONLY("/proc/irq"), PROC_READ_ONLY("/proc/scsi"), - { "mqueue", "/dev/mqueue", "mqueue", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "mqueue", "/dev/mqueue", "mqueue", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_IN_USERNS|MOUNT_MKDIR }, /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */ - { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/tmp", "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME, MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR }, - { "tmpfs", "/sys", "tmpfs", "mode=555", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "tmpfs", "/sys", "tmpfs", "mode=555" TMPFS_LIMITS_SYS, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR }, - { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR }, /* skipped if above was mounted */ - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_MKDIR }, /* skipped if above was mounted */ - { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, + { "tmpfs", "/dev", "tmpfs", "mode=755" TMPFS_LIMITS_DEV, MS_NOSUID|MS_STRICTATIME, MOUNT_FATAL|MOUNT_MKDIR }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS, MS_NOSUID|MS_NODEV|MS_STRICTATIME, MOUNT_FATAL|MOUNT_MKDIR }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + { "tmpfs", "/run", "tmpfs", "mode=755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME, MOUNT_FATAL|MOUNT_MKDIR }, - + { "/run/host", "/run/host", NULL, NULL, MS_BIND, + MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PREFIX_ROOT }, /* Prepare this so that we can make it read-only when we are done */ + { "/etc/os-release", "/run/host/os-release", NULL, NULL, MS_BIND, + MOUNT_TOUCH }, /* As per kernel interface requirements, bind mount first (creating mount points) and make read-only later */ + { "/usr/lib/os-release", "/run/host/os-release", NULL, NULL, MS_BIND, + MOUNT_FATAL }, /* If /etc/os-release doesn't exist use the version in /usr/lib as fallback */ + { NULL, "/run/host/os-release", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, + MOUNT_FATAL }, + { NULL, "/run/host", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, + MOUNT_FATAL|MOUNT_IN_USERNS }, #if HAVE_SELINUX - { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, MOUNT_MKDIR }, /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */ - { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, + { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, 0 }, /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */ #endif }; @@ -634,9 +590,9 @@ int mount_all(const char *dest, int r; for (k = 0; k < ELEMENTSOF(mount_table); k++) { - _cleanup_free_ char *where = NULL, *options = NULL; - const char *o; + _cleanup_free_ char *where = NULL, *options = NULL, *prefixed = NULL; bool fatal = FLAGS_SET(mount_table[k].mount_settings, MOUNT_FATAL); + const char *o; if (in_userns != FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS)) continue; @@ -663,8 +619,13 @@ int mount_all(const char *dest, continue; } - if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_MKDIR)) { - r = mkdir_userns_p(dest, where, 0755, (use_userns && !in_userns) ? uid_shift : UID_INVALID); + if ((mount_table[k].mount_settings & (MOUNT_MKDIR|MOUNT_TOUCH)) != 0) { + uid_t u = (use_userns && !in_userns) ? uid_shift : UID_INVALID; + + if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH)) + r = mkdir_parents_safe(dest, where, 0755, u, u, 0); + else + r = mkdir_p_safe(dest, where, 0755, u, u, 0); if (r < 0 && r != -EEXIST) { if (fatal && r != -EROFS) return log_error_errno(r, "Failed to create directory %s: %m", where); @@ -678,6 +639,18 @@ int mount_all(const char *dest, } } + if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH)) { + r = touch(where); + if (r < 0 && r != -EEXIST) { + if (fatal && r != -EROFS) + return log_error_errno(r, "Failed to create file %s: %m", where); + + log_debug_errno(r, "Failed to create file %s: %m", where); + if (r != -EROFS) + continue; + } + } + o = mount_table[k].options; if (streq_ptr(mount_table[k].type, "tmpfs")) { r = tmpfs_patch_options(o, in_userns ? 0 : uid_shift, selinux_apifs_context, &options); @@ -687,8 +660,18 @@ int mount_all(const char *dest, o = options; } + if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_PREFIX_ROOT)) { + /* Optionally prefix the mount source with the root dir. This is useful in bind + * mounts to be created within the container image before we transition into it. Note + * that MOUNT_IN_USERNS is run after we transitioned hence prefixing is not ncessary + * for those. */ + r = chase_symlinks(mount_table[k].what, dest, CHASE_PREFIX_ROOT, &prefixed, NULL); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].what); + } + r = mount_verbose(fatal ? LOG_ERR : LOG_DEBUG, - mount_table[k].what, + prefixed ?: mount_table[k].what, where, mount_table[k].type, mount_table[k].flags, @@ -898,7 +881,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) { return m->graceful ? 0 : r; } - r = mode_to_inaccessible_node("/run/systemd", st.st_mode, &source); + r = mode_to_inaccessible_node(NULL, st.st_mode, &source); if (r < 0) return m->graceful ? 0 : r; @@ -1023,7 +1006,7 @@ static int setup_volatile_state(const char *directory, uid_t uid_shift, const ch if (r < 0 && errno != EEXIST) return log_error_errno(errno, "Failed to create %s: %m", directory); - options = "mode=755"; + options = "mode=755" TMPFS_LIMITS_VOLATILE_STATE; r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); if (r < 0) return log_oom(); @@ -1068,7 +1051,7 @@ static int setup_volatile_yes(const char *directory, uid_t uid_shift, const char if (!mkdtemp(template)) return log_error_errno(errno, "Failed to create temporary directory: %m"); - options = "mode=755"; + options = "mode=755" TMPFS_LIMITS_ROOTFS; r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); if (r < 0) goto fail; @@ -1135,7 +1118,7 @@ static int setup_volatile_overlay(const char *directory, uid_t uid_shift, const if (!mkdtemp(template)) return log_error_errno(errno, "Failed to create temporary directory: %m"); - options = "mode=755"; + options = "mode=755" TMPFS_LIMITS_ROOTFS; r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); if (r < 0) goto finish; diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index e8bb90383..3898c74f1 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -17,6 +17,8 @@ typedef enum MountSettingsMask { MOUNT_ROOT_ONLY = 1 << 6, /* if set, only root mounts are mounted */ MOUNT_NON_ROOT_ONLY = 1 << 7, /* if set, only non-root mounts are mounted */ MOUNT_MKDIR = 1 << 8, /* if set, make directory to mount over first */ + MOUNT_TOUCH = 1 << 9, /* if set, touch file to mount over first */ + MOUNT_PREFIX_ROOT = 1 << 10,/* if set, prefix the source path with the container's root directory */ } MountSettingsMask; typedef enum CustomMountType { diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 782c03c53..e3ade9237 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -51,13 +51,13 @@ * cgrouspv1 crap: kernel, kernelTCP, swapiness, disableOOMKiller, swap, devices, leafWeight * general: it shouldn't leak lower level abstractions this obviously * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod - * needs to say what happense when some option is not specified, i.e. which defautls apply + * needs to say what happense when some option is not specified, i.e. which defaults apply * no architecture? no personality? * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO". * spec should say what to do with unknown props * /bin/mount regarding NFS and FUSE required? * what does terminal=false mean? - * sysctl inside or outside? whitelisting? + * sysctl inside or outside? allow-listing? * swapiness typo -> swappiness * * Unsupported: @@ -1029,39 +1029,40 @@ static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlag return r; if (!data.allow) { - /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' vs. 'deny' for the - * devices cgroup controller is really not about whitelisting and blacklisting but about adding - * and removing entries from the whitelist. Since we always start out with an empty whitelist - * we hence ignore the whole thing, as removing entries which don't exist make no sense. We'll - * log about this, since this is really borked in the spec, with one exception: the entry - * that's supposed to drop the kernel's default we ignore silently */ + /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' + * vs. 'deny' for the devices cgroup controller is really not about allow-listing and + * deny-listing but about adding and removing entries from the allow list. Since we + * always start out with an empty allow list we hence ignore the whole thing, as + * removing entries which don't exist make no sense. We'll log about this, since this + * is really borked in the spec, with one exception: the entry that's supposed to + * drop the kernel's default we ignore silently */ if (!data.r || !data.w || !data.m || data.type != 0 || data.major != (unsigned) -1 || data.minor != (unsigned) -1) - json_log(v, flags|JSON_WARNING, 0, "Devices cgroup whitelist with arbitrary 'allow' entries not supported, ignoring."); + json_log(v, flags|JSON_WARNING, 0, "Devices cgroup allow list with arbitrary 'allow' entries not supported, ignoring."); /* We ignore the 'deny' entry as for us that's implied */ continue; } if (!data.r && !data.w && !data.m) { - json_log(v, flags|LOG_WARNING, 0, "Device cgroup whitelist entry with no effect found, ignoring."); + json_log(v, flags|LOG_WARNING, 0, "Device cgroup allow list entry with no effect found, ignoring."); continue; } if (data.minor != (unsigned) -1 && data.major == (unsigned) -1) return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), - "Device cgroup whitelist entries with minors but no majors not supported."); + "Device cgroup allow list entries with minors but no majors not supported."); if (data.major != (unsigned) -1 && data.type == 0) return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), - "Device cgroup whitelist entries with majors but no device node type not supported."); + "Device cgroup allow list entries with majors but no device node type not supported."); if (data.type == 0) { - if (data.r && data.w && data.m) /* a catchall whitelist entry means we are looking at a noop */ + if (data.r && data.w && data.m) /* a catchall allow list entry means we are looking at a noop */ noop = true; else return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), - "Device cgroup whitelist entries with no type not supported."); + "Device cgroup allow list entries with no type not supported."); } a = reallocarray(list, n_list + 1, sizeof(struct device_data)); diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index fc591e272..112c3562a 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -8,6 +8,7 @@ #include "acl-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "missing_magic.h" #include "nspawn-def.h" @@ -335,12 +336,11 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift donate_fd = true; } - d = fdopendir(fd); + d = take_fdopendir(&fd); if (!d) { r = -errno; goto finish; } - fd = -1; FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { struct stat fst; diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 9b7ca5e3d..50867f384 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -3,6 +3,7 @@ #include "sd-bus.h" #include "bus-error.h" +#include "bus-locator.h" #include "bus-unit-util.h" #include "bus-util.h" #include "bus-wait-for-jobs.h" @@ -122,11 +123,9 @@ int register_machine( assert(bus); if (keep_unit) { - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", + bus_machine_mgr, "RegisterMachineWithNetwork", &error, NULL, @@ -141,13 +140,7 @@ int register_machine( } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CreateMachineWithNetwork"); + r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CreateMachineWithNetwork"); if (r < 0) return bus_log_create_error(r); @@ -218,16 +211,7 @@ int unregister_machine( assert(bus); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "UnregisterMachine", - &error, - NULL, - "s", - machine_name); + r = bus_call_method(bus, bus_machine_mgr, "UnregisterMachine", &error, NULL, "s", machine_name); if (r < 0) log_debug("Failed to unregister machine: %s", bus_error_message(&error, r)); @@ -262,13 +246,7 @@ int allocate_scope( if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -354,26 +332,15 @@ int terminate_scope( if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "AbandonScope", - &error, - NULL, - "s", - scope); + r = bus_call_method(bus, bus_systemd_mgr, "AbandonScope", &error, NULL, "s", scope); if (r < 0) { log_debug_errno(r, "Failed to abandon scope '%s', ignoring: %s", scope, bus_error_message(&error, r)); sd_bus_error_free(&error); } - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "KillUnit", &error, NULL, @@ -386,16 +353,7 @@ int terminate_scope( sd_bus_error_free(&error); } - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnrefUnit", - &error, - NULL, - "s", - scope); + r = bus_call_method(bus, bus_systemd_mgr, "UnrefUnit", &error, NULL, "s", scope); if (r < 0) log_debug_errno(r, "Failed to drop reference to scope '%s', ignoring: %s", scope, bus_error_message(&error, r)); diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c index f94f131f2..79110d90d 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -25,13 +25,13 @@ static int seccomp_add_default_syscall_filter( scmp_filter_ctx ctx, uint32_t arch, uint64_t cap_list_retain, - char **syscall_whitelist, - char **syscall_blacklist) { + char **syscall_allow_list, + char **syscall_deny_list) { static const struct { uint64_t capability; const char* name; - } whitelist[] = { + } allow_list[] = { /* Let's use set names where we can */ { 0, "@aio" }, { 0, "@basic-io" }, @@ -142,17 +142,17 @@ static int seccomp_add_default_syscall_filter( char **p; int r; - for (size_t i = 0; i < ELEMENTSOF(whitelist); i++) { - if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0) + for (size_t i = 0; i < ELEMENTSOF(allow_list); i++) { + if (allow_list[i].capability != 0 && (cap_list_retain & (1ULL << allow_list[i].capability)) == 0) continue; - r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist, false); + r = seccomp_add_syscall_filter_item(ctx, allow_list[i].name, SCMP_ACT_ALLOW, syscall_deny_list, false); if (r < 0) - return log_error_errno(r, "Failed to add syscall filter item %s: %m", whitelist[i].name); + return log_error_errno(r, "Failed to add syscall filter item %s: %m", allow_list[i].name); } - STRV_FOREACH(p, syscall_whitelist) { - r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, true); + STRV_FOREACH(p, syscall_allow_list) { + r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_deny_list, true); if (r < 0) log_warning_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch)); @@ -161,7 +161,7 @@ static int seccomp_add_default_syscall_filter( return 0; } -int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) { +int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_list, char **syscall_deny_list) { uint32_t arch; int r; @@ -173,13 +173,13 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; - log_debug("Applying whitelist on architecture: %s", seccomp_arch_to_string(arch)); + log_debug("Applying allow list on architecture: %s", seccomp_arch_to_string(arch)); r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(EPERM)); if (r < 0) return log_error_errno(r, "Failed to allocate seccomp object: %m"); - r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist); + r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_allow_list, syscall_deny_list); if (r < 0) return r; @@ -231,7 +231,7 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys #else -int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) { +int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_list, char **syscall_deny_list) { return 0; } diff --git a/src/nspawn/nspawn-seccomp.h b/src/nspawn/nspawn-seccomp.h index d852eef63..417432352 100644 --- a/src/nspawn/nspawn-seccomp.h +++ b/src/nspawn/nspawn-seccomp.h @@ -3,4 +3,4 @@ #include -int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist); +int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_ist, char **syscall_deny_list); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 5fb5b49bb..d341fa25a 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -78,7 +78,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) { "Files\0", config_item_perf_lookup, nspawn_gperf_lookup, CONFIG_PARSE_WARN, - s); + s, NULL); if (r < 0) return r; @@ -129,8 +129,8 @@ Settings* settings_free(Settings *s) { free(s->pivot_root_new); free(s->pivot_root_old); free(s->working_directory); - strv_free(s->syscall_whitelist); - strv_free(s->syscall_blacklist); + strv_free(s->syscall_allow_list); + strv_free(s->syscall_deny_list); rlimit_free_all(s->rlimit); free(s->hostname); cpu_set_reset(&s->cpu_set); @@ -295,35 +295,6 @@ int config_parse_capability( return 0; } -int config_parse_id128( - 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) { - - sd_id128_t t, *result = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = sd_id128_from_string(rvalue, &t); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); - return 0; - } - - *result = t; - return 0; -} - int config_parse_pivot_root( const char *unit, const char *filename, @@ -718,9 +689,9 @@ int config_parse_syscall_filter( } if (negative) - r = strv_extend(&settings->syscall_blacklist, word); + r = strv_extend(&settings->syscall_deny_list, word); else - r = strv_extend(&settings->syscall_whitelist, word); + r = strv_extend(&settings->syscall_allow_list, word); if (r < 0) return log_oom(); } @@ -821,8 +792,16 @@ static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = { [RESOLV_CONF_OFF] = "off", [RESOLV_CONF_COPY_HOST] = "copy-host", [RESOLV_CONF_COPY_STATIC] = "copy-static", + [RESOLV_CONF_COPY_UPLINK] = "copy-uplink", + [RESOLV_CONF_COPY_STUB] = "copy-stub", + [RESOLV_CONF_REPLACE_HOST] = "replace-host", + [RESOLV_CONF_REPLACE_STATIC] = "replace-static", + [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink", + [RESOLV_CONF_REPLACE_STUB] = "replace-stub", [RESOLV_CONF_BIND_HOST] = "bind-host", [RESOLV_CONF_BIND_STATIC] = "bind-static", + [RESOLV_CONF_BIND_UPLINK] = "bind-uplink", + [RESOLV_CONF_BIND_STUB] = "bind-stub", [RESOLV_CONF_DELETE] = "delete", [RESOLV_CONF_AUTO] = "auto", }; diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index f1a1a7546..ab31c05a9 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -38,10 +38,18 @@ typedef enum UserNamespaceMode { typedef enum ResolvConfMode { RESOLV_CONF_OFF, - RESOLV_CONF_COPY_HOST, - RESOLV_CONF_COPY_STATIC, + RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */ + RESOLV_CONF_COPY_STATIC, /* /usr/lib/systemd/resolv.conf */ + RESOLV_CONF_COPY_UPLINK, /* /run/systemd/resolve/resolv.conf */ + RESOLV_CONF_COPY_STUB, /* /run/systemd/resolve/stub-resolv.conf */ + RESOLV_CONF_REPLACE_HOST, + RESOLV_CONF_REPLACE_STATIC, + RESOLV_CONF_REPLACE_UPLINK, + RESOLV_CONF_REPLACE_STUB, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC, + RESOLV_CONF_BIND_UPLINK, + RESOLV_CONF_BIND_STUB, RESOLV_CONF_DELETE, RESOLV_CONF_AUTO, _RESOLV_CONF_MODE_MAX, @@ -157,8 +165,8 @@ typedef struct Settings { UserNamespaceMode userns_mode; uid_t uid_shift, uid_range; bool notify_ready; - char **syscall_whitelist; - char **syscall_blacklist; + char **syscall_allow_list; + char **syscall_deny_list; struct rlimit *rlimit[_RLIMIT_MAX]; char *hostname; int no_new_privileges; @@ -226,7 +234,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free); const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, GPERF_LEN_TYPE length); CONFIG_PARSER_PROTOTYPE(config_parse_capability); -CONFIG_PARSER_PROTOTYPE(config_parse_id128); CONFIG_PARSER_PROTOTYPE(config_parse_expose_port); CONFIG_PARSER_PROTOTYPE(config_parse_volatile_mode); CONFIG_PARSER_PROTOTYPE(config_parse_pivot_root); diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index cb2b2272b..d0e575fef 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -118,10 +118,9 @@ int change_uid_gid(const char *user, char **_home) { if (fd < 0) return fd; - f = fdopen(fd, "r"); + f = take_fdopen(&fd, "r"); if (!f) return log_oom(); - fd = -1; r = read_line(f, LONG_LINE_MAX, &line); if (r == 0) @@ -191,10 +190,9 @@ int change_uid_gid(const char *user, char **_home) { if (fd < 0) return fd; - f = fdopen(fd, "r"); + f = take_fdopen(&fd, "r"); if (!f) return log_oom(); - fd = -1; r = read_line(f, LONG_LINE_MAX, &line); if (r == 0) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index eaf2d7977..3b9493f23 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -79,6 +79,7 @@ #include "ptyfwd.h" #include "random-util.h" #include "raw-clone.h" +#include "resolve-util.h" #include "rlimit-util.h" #include "rm-rf.h" #if HAVE_SECCOMP @@ -100,12 +101,6 @@ #include "user-util.h" #include "util.h" -#if HAVE_SPLIT_USR -#define STATIC_RESOLV_CONF "/lib/systemd/resolv.conf" -#else -#define STATIC_RESOLV_CONF "/usr/lib/systemd/resolv.conf" -#endif - /* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path * nspawn_notify_socket_path is relative to the container * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ @@ -204,9 +199,13 @@ static bool arg_use_cgns = true; static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS; static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP; static void *arg_root_hash = NULL; +static char *arg_verity_data = NULL; +static char *arg_root_hash_sig_path = NULL; +static void *arg_root_hash_sig = NULL; +static size_t arg_root_hash_sig_size = 0; static size_t arg_root_hash_size = 0; -static char **arg_syscall_whitelist = NULL; -static char **arg_syscall_blacklist = NULL; +static char **arg_syscall_allow_list = NULL; +static char **arg_syscall_deny_list = NULL; #if HAVE_SECCOMP static scmp_filter_ctx arg_seccomp = NULL; #endif @@ -247,8 +246,11 @@ STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); -STATIC_DESTRUCTOR_REGISTER(arg_syscall_whitelist, strv_freep); -STATIC_DESTRUCTOR_REGISTER(arg_syscall_blacklist, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep); +STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep); #if HAVE_SECCOMP STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep); #endif @@ -308,6 +310,11 @@ static int help(void) { " --read-only Mount the root directory read-only\n" " --volatile[=MODE] Run the system in volatile mode\n" " --root-hash=HASH Specify verity root hash for root disk image\n" + " --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n" + " as a DER encoded PKCS7, either as a path to a file\n" + " or as an ASCII base64 encoded string prefixed by\n" + " 'base64:'\n" + " --verity-data=PATH Specify hash device for verity\n" " --pivot-root=PATH[:PATH]\n" " Pivot root to given directory in the container\n\n" "%3$sExecution:%4$s\n" @@ -668,6 +675,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_PIPE, ARG_OCI_BUNDLE, ARG_NO_PAGER, + ARG_VERITY_DATA, + ARG_ROOT_HASH_SIG, }; static const struct option options[] = { @@ -733,6 +742,8 @@ static int parse_argv(int argc, char *argv[]) { { "pipe", no_argument, NULL, ARG_PIPE }, { "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "verity-data", required_argument, NULL, ARG_VERITY_DATA }, + { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG }, {} }; @@ -1321,6 +1332,37 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_VERITY_DATA: + r = parse_path_argument_and_warn(optarg, false, &arg_verity_data); + if (r < 0) + return r; + break; + + case ARG_ROOT_HASH_SIG: { + char *value; + + if ((value = startswith(optarg, "base64:"))) { + void *p; + size_t l; + + r = unbase64mem(value, strlen(value), &p, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg); + + free_and_replace(arg_root_hash_sig, p); + arg_root_hash_sig_size = l; + arg_root_hash_sig_path = mfree(arg_root_hash_sig_path); + } else { + r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path); + if (r < 0) + return r; + arg_root_hash_sig = mfree(arg_root_hash_sig); + arg_root_hash_sig_size = 0; + } + + break; + } + case ARG_SYSTEM_CALL_FILTER: { bool negative; const char *items; @@ -1340,9 +1382,9 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse system call filter: %m"); if (negative) - r = strv_extend(&arg_syscall_blacklist, word); + r = strv_extend(&arg_syscall_deny_list, word); else - r = strv_extend(&arg_syscall_whitelist, word); + r = strv_extend(&arg_syscall_allow_list, word); if (r < 0) return log_oom(); } @@ -1850,12 +1892,13 @@ static int setup_resolv_conf(const char *dest) { if (arg_resolv_conf == RESOLV_CONF_AUTO) { if (arg_private_network) m = RESOLV_CONF_OFF; - else if (have_resolv_conf(STATIC_RESOLV_CONF) > 0 && resolved_listening() > 0) - m = etc_writable() ? RESOLV_CONF_COPY_STATIC : RESOLV_CONF_BIND_STATIC; + else if (have_resolv_conf(PRIVATE_STUB_RESOLV_CONF) > 0 && resolved_listening() > 0) + m = etc_writable() ? RESOLV_CONF_COPY_STUB : RESOLV_CONF_BIND_STUB; else if (have_resolv_conf("/etc/resolv.conf") > 0) m = etc_writable() ? RESOLV_CONF_COPY_HOST : RESOLV_CONF_BIND_HOST; else m = etc_writable() ? RESOLV_CONF_DELETE : RESOLV_CONF_OFF; + } else m = arg_resolv_conf; @@ -1877,12 +1920,16 @@ static int setup_resolv_conf(const char *dest) { return 0; } - if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_COPY_STATIC)) - what = STATIC_RESOLV_CONF; + if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_COPY_STATIC)) + what = PRIVATE_STATIC_RESOLV_CONF; + else if (IN_SET(m, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_COPY_UPLINK)) + what = PRIVATE_UPLINK_RESOLV_CONF; + else if (IN_SET(m, RESOLV_CONF_BIND_STUB, RESOLV_CONF_REPLACE_STUB, RESOLV_CONF_COPY_STUB)) + what = PRIVATE_STUB_RESOLV_CONF; else what = "/etc/resolv.conf"; - if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC)) { + if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_BIND_STUB)) { _cleanup_free_ char *resolved = NULL; int found; @@ -1898,17 +1945,22 @@ static int setup_resolv_conf(const char *dest) { r = mount_verbose(LOG_WARNING, what, resolved, NULL, MS_BIND, NULL); if (r >= 0) return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL); + + /* If that didn't work, let's copy the file */ } - /* If that didn't work, let's copy the file */ - r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK); + if (IN_SET(m, RESOLV_CONF_REPLACE_HOST, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_REPLACE_STUB)) + r = copy_file_atomic(what, where, 0644, 0, 0, COPY_REFLINK|COPY_REPLACE); + else + r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK); if (r < 0) { /* If the file already exists as symlink, let's suppress the warning, under the assumption that * resolved or something similar runs inside and the symlink points there. * * If the disk image is read-only, there's also no point in complaining. */ - log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC) && IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) && + IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); return 0; } @@ -2159,10 +2211,11 @@ static int setup_dev_console(const char *console) { static int setup_keyring(void) { key_serial_t keyring; - /* Allocate a new session keyring for the container. This makes sure the keyring of the session systemd-nspawn - * was invoked from doesn't leak into the container. Note that by default we block keyctl() and request_key() - * anyway via seccomp so doing this operation isn't strictly necessary, but in case people explicitly whitelist - * these system calls let's make sure we don't leak anything into the container. */ + /* Allocate a new session keyring for the container. This makes sure the keyring of the session + * systemd-nspawn was invoked from doesn't leak into the container. Note that by default we block + * keyctl() and request_key() anyway via seccomp so doing this operation isn't strictly necessary, + * but in case people explicitly allow-list these system calls let's make sure we don't leak anything + * into the container. */ keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0); if (keyring == -1) { @@ -2878,7 +2931,8 @@ static int inner_child( int kmsg_socket, int rtnl_socket, int master_pty_socket, - FDSet *fds) { + FDSet *fds, + char **os_release_pairs) { _cleanup_free_ char *home = NULL; char as_uuid[ID128_UUID_STRING_MAX]; @@ -2976,13 +3030,10 @@ static int inner_child( arg_uid_range, arg_selinux_apifs_context, true); - if (r < 0) - return r; - } else { + } else r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); - if (r < 0) - return r; - } + if (r < 0) + return r; r = setup_boot_id(); if (r < 0) @@ -3083,7 +3134,7 @@ static int inner_child( } else #endif { - r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist); + r = setup_seccomp(arg_caps_retain, arg_syscall_allow_list, arg_syscall_deny_list); if (r < 0) return r; } @@ -3147,7 +3198,7 @@ static int inner_child( if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) return log_oom(); - env_use = strv_env_merge(2, envp, arg_setenv); + env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv); if (!env_use) return log_oom(); @@ -3273,6 +3324,7 @@ static int outer_child( FDSet *fds, int netns_fd) { + _cleanup_strv_free_ char **os_release_pairs = NULL; _cleanup_close_ int fd = -1; const char *p; pid_t pid; @@ -3294,6 +3346,10 @@ static int outer_child( log_debug("Outer child is initializing."); + r = load_os_release_pairs_with_prefix("/", "container_host_", &os_release_pairs); + if (r < 0) + log_debug_errno(r, "Failed to read os-release from host for container, ignoring: %m"); + if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); @@ -3475,7 +3531,7 @@ static int outer_child( (void) dev_setup(directory, arg_uid_shift, arg_uid_shift); - p = prefix_roota(directory, "/run/systemd"); + p = prefix_roota(directory, "/run"); (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift); r = setup_pts(directory); @@ -3557,7 +3613,7 @@ static int outer_child( return log_error_errno(r, "Failed to join network namespace: %m"); } - r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds); + r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds, os_release_pairs); if (r < 0) _exit(EXIT_FAILURE); @@ -3690,19 +3746,15 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r .iov_base = buf, .iov_len = sizeof(buf)-1, }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = &control, .msg_controllen = sizeof(control), }; - struct cmsghdr *cmsg; - struct ucred *ucred = NULL; + struct ucred *ucred; ssize_t n; pid_t inner_child_pid; _cleanup_strv_free_ char **tags = NULL; @@ -3724,15 +3776,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r cmsg_close_all(&msghdr); - CMSG_FOREACH(cmsg, &msghdr) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { - - ucred = (struct ucred*) CMSG_DATA(cmsg); - } - } - + ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); if (!ucred || ucred->pid != inner_child_pid) { log_debug("Received notify message without valid credentials. Ignoring."); return 0; @@ -3958,11 +4002,11 @@ static int merge_settings(Settings *settings, const char *path) { if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) { - if (!arg_settings_trusted && !strv_isempty(settings->syscall_whitelist)) + if (!arg_settings_trusted && !strv_isempty(settings->syscall_allow_list)) log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path); else { - strv_free_and_replace(arg_syscall_whitelist, settings->syscall_whitelist); - strv_free_and_replace(arg_syscall_blacklist, settings->syscall_blacklist); + strv_free_and_replace(arg_syscall_allow_list, settings->syscall_allow_list); + strv_free_and_replace(arg_syscall_deny_list, settings->syscall_deny_list); } #if HAVE_SECCOMP @@ -4597,7 +4641,7 @@ static int run_container( if (!barrier_place_and_sync(&barrier)) /* #5 */ return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early."); - /* At this point we have made use of the UID we picked, and thus nss-mymachines + /* At this point we have made use of the UID we picked, and thus nss-systemd/systemd-machined.service * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */ etc_passwd_lock = safe_close(etc_passwd_lock); @@ -4822,6 +4866,58 @@ static int initialize_rlimits(void) { return 0; } +static int cant_be_in_netns(void) { + union sockaddr_union sa = { + .un = { + .sun_family = AF_UNIX, + .sun_path = "/run/udev/control", + }, + }; + char udev_path[STRLEN("/proc//ns/net") + DECIMAL_STR_MAX(pid_t)]; + _cleanup_free_ char *udev_ns = NULL, *our_ns = NULL; + _cleanup_close_ int fd = -1; + struct ucred ucred; + int r; + + /* Check if we are in the same netns as udev. If we aren't, then device monitoring (and thus waiting + * for loopback block devices) won't work, and we will hang. Detect this case and exit early with a + * nice message. */ + + if (!arg_image) /* only matters if --image= us used, i.e. we actually need to use loopback devices */ + return 0; + + fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate udev control socket: %m"); + + if (connect(fd, &sa.un, SOCKADDR_UN_LEN(sa.un)) < 0) { + + if (errno == ENOENT || ERRNO_IS_DISCONNECT(errno)) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Sorry, but --image= requires access to the host's /run/ hierarchy, since we need access to udev."); + + return log_error_errno(errno, "Failed to connect socket to udev control socket: %m"); + } + + r = getpeercred(fd, &ucred); + if (r < 0) + return log_error_errno(r, "Failed to determine peer of udev control socket: %m"); + + xsprintf(udev_path, "/proc/" PID_FMT "/ns/net", ucred.pid); + r = readlink_malloc(udev_path, &udev_ns); + if (r < 0) + return log_error_errno(r, "Failed to read network namespace of udev: %m"); + + r = readlink_malloc("/proc/self/ns/net", &our_ns); + if (r < 0) + return log_error_errno(r, "Failed to read our own network namespace: %m"); + + if (!streq(our_ns, udev_ns)) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Sorry, but --image= is only supported in the main network namespace, since we need access to udev/AF_NETLINK."); + return 0; +} + static int run(int argc, char *argv[]) { bool secondary = false, remove_directory = false, remove_image = false, veth_created = false, remove_tmprootdir = false; @@ -4848,6 +4944,10 @@ static int run(int argc, char *argv[]) { if (r < 0) goto finish; + r = cant_be_in_netns(); + if (r < 0) + goto finish; + r = initialize_rlimits(); if (r < 0) goto finish; @@ -4934,7 +5034,7 @@ static int run(int argc, char *argv[]) { } /* We take an exclusive lock on this image, since it's our private, ephemeral copy - * only owned by us and noone else. */ + * only owned by us and no one else. */ r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock); if (r < 0) { log_error_errno(r, "Failed to lock %s: %m", np); @@ -5039,6 +5139,7 @@ static int run(int argc, char *argv[]) { } } else { + DissectImageFlags dissect_image_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK; assert(arg_image); assert(!arg_template); @@ -5088,13 +5189,14 @@ static int run(int argc, char *argv[]) { goto finish; } - if (!arg_root_hash) { - r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size); - if (r < 0) { - log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image); - goto finish; - } + r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size, + arg_verity_data ? NULL : &arg_verity_data, + arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path); + if (r < 0) { + log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image); + goto finish; } + dissect_image_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; } if (!mkdtemp(tmprootdir)) { @@ -5120,7 +5222,8 @@ static int run(int argc, char *argv[]) { loop->fd, arg_image, arg_root_hash, arg_root_hash_size, - DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, + arg_verity_data, + dissect_image_flags, &dissected_image); if (r == -ENOPKG) { /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */ @@ -5138,7 +5241,7 @@ static int run(int argc, char *argv[]) { if (!arg_root_hash && dissected_image->can_verity) log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image); - r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image); + r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, 0, &decrypted_image); if (r < 0) goto finish; diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 8c16b7f36..9aa6debc1 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -77,7 +77,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( return NSS_STATUS_TRYAGAIN; } - /* We respond to our local host name, our hostname suffixed with a single dot. */ + /* We respond to our local hostname, our hostname suffixed with a single dot. */ if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) goto not_found; diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 364356da5..5db0dcef7 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -8,6 +8,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-locator.h" #include "env-util.h" #include "errno-util.h" #include "format-util.h" @@ -18,15 +19,11 @@ #include "nss-util.h" #include "signal-util.h" #include "string-util.h" -#include "user-util.h" NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); NSS_GETGR_PROTOTYPES(mymachines); -#define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000)) -#define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000)) - static int count_addresses(sd_bus_message *m, int af, unsigned *ret) { unsigned c = 0; int r; @@ -128,14 +125,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( if (r < 0) goto fail; - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name); if (r < 0) goto fail; @@ -287,14 +277,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( if (r < 0) goto fail; - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); + r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name); if (r < 0) goto fail; @@ -415,102 +398,7 @@ enum nss_status _nss_mymachines_getpwnam_r( char *buffer, size_t buflen, int *errnop) { - _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 *p, *e, *machine; - uint32_t mapped; - uid_t uid; - size_t l; - int r; - - PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(pwd); - - p = startswith(name, "vu-"); - if (!p) - return NSS_STATUS_NOTFOUND; - - e = strrchr(p, '-'); - if (!e || e == p) - return NSS_STATUS_NOTFOUND; - - if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ - return NSS_STATUS_NOTFOUND; - - r = parse_uid(e + 1, &uid); - if (r < 0) - return NSS_STATUS_NOTFOUND; - - machine = strndupa(p, e - p); - if (!machine_name_is_valid(machine)) - return NSS_STATUS_NOTFOUND; - - if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) - /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve - * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus - * running on the host. */ - return NSS_STATUS_NOTFOUND; - - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapFromMachineUser", - &error, - &reply, - "su", - machine, (uint32_t) uid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) - return NSS_STATUS_NOTFOUND; - - goto fail; - } - - r = sd_bus_message_read(reply, "u", &mapped); - if (r < 0) - goto fail; - - /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */ - if (mapped < HOST_UID_LIMIT || mapped == uid) - return NSS_STATUS_NOTFOUND; - - l = strlen(name); - if (buflen < l+1) { - UNPROTECT_ERRNO; - *errnop = ERANGE; - return NSS_STATUS_TRYAGAIN; - } - - memcpy(buffer, name, l+1); - - pwd->pw_name = buffer; - pwd->pw_uid = mapped; - pwd->pw_gid = GID_NOBODY; - pwd->pw_gecos = buffer; - pwd->pw_passwd = (char*) "*"; /* locked */ - pwd->pw_dir = (char*) "/"; - pwd->pw_shell = (char*) NOLOGIN; - - return NSS_STATUS_SUCCESS; - -fail: - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } enum nss_status _nss_mymachines_getpwuid_r( @@ -519,178 +407,16 @@ enum nss_status _nss_mymachines_getpwuid_r( char *buffer, size_t buflen, int *errnop) { - _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 *machine; - uint32_t mapped; - int r; - - PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - if (!uid_is_valid(uid)) - return NSS_STATUS_NOTFOUND; - - /* We consider all uids < 65536 host uids */ - if (uid < HOST_UID_LIMIT) - return NSS_STATUS_NOTFOUND; - - if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) - return NSS_STATUS_NOTFOUND; - - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapToMachineUser", - &error, - &reply, - "u", - (uint32_t) uid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) - return NSS_STATUS_NOTFOUND; - - goto fail; - } - - r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped); - if (r < 0) - goto fail; - - if (mapped == uid) - return NSS_STATUS_NOTFOUND; - - if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { - UNPROTECT_ERRNO; - *errnop = ERANGE; - return NSS_STATUS_TRYAGAIN; - } - - pwd->pw_name = buffer; - pwd->pw_uid = uid; - pwd->pw_gid = GID_NOBODY; - pwd->pw_gecos = buffer; - pwd->pw_passwd = (char*) "*"; /* locked */ - pwd->pw_dir = (char*) "/"; - pwd->pw_shell = (char*) NOLOGIN; - - return NSS_STATUS_SUCCESS; - -fail: - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } -#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess" - enum nss_status _nss_mymachines_getgrnam_r( const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop) { - _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 *p, *e, *machine; - uint32_t mapped; - uid_t gid; - size_t l; - int r; - - PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(gr); - - p = startswith(name, "vg-"); - if (!p) - return NSS_STATUS_NOTFOUND; - - e = strrchr(p, '-'); - if (!e || e == p) - return NSS_STATUS_NOTFOUND; - - if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ - return NSS_STATUS_NOTFOUND; - - r = parse_gid(e + 1, &gid); - if (r < 0) - return NSS_STATUS_NOTFOUND; - - machine = strndupa(p, e - p); - if (!machine_name_is_valid(machine)) - return NSS_STATUS_NOTFOUND; - - if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) - return NSS_STATUS_NOTFOUND; - - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapFromMachineGroup", - &error, - &reply, - "su", - machine, (uint32_t) gid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) - return NSS_STATUS_NOTFOUND; - - goto fail; - } - - r = sd_bus_message_read(reply, "u", &mapped); - if (r < 0) - goto fail; - - if (mapped < HOST_GID_LIMIT || mapped == gid) - return NSS_STATUS_NOTFOUND; - - l = sizeof(char*) + strlen(name) + 1; - if (buflen < l) { - UNPROTECT_ERRNO; - *errnop = ERANGE; - return NSS_STATUS_TRYAGAIN; - } - - memzero(buffer, sizeof(char*)); - strcpy(buffer + sizeof(char*), name); - - gr->gr_name = buffer + sizeof(char*); - gr->gr_gid = mapped; - gr->gr_passwd = (char*) "*"; /* locked */ - gr->gr_mem = (char**) buffer; - - return NSS_STATUS_SUCCESS; - -fail: - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } enum nss_status _nss_mymachines_getgrgid_r( @@ -699,80 +425,5 @@ enum nss_status _nss_mymachines_getgrgid_r( char *buffer, size_t buflen, int *errnop) { - _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 *machine; - uint32_t mapped; - int r; - - PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - if (!gid_is_valid(gid)) - return NSS_STATUS_NOTFOUND; - - /* We consider all gids < 65536 host gids */ - if (gid < HOST_GID_LIMIT) - return NSS_STATUS_NOTFOUND; - - if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) - return NSS_STATUS_NOTFOUND; - - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapToMachineGroup", - &error, - &reply, - "u", - (uint32_t) gid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) - return NSS_STATUS_NOTFOUND; - - goto fail; - } - - r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped); - if (r < 0) - goto fail; - - if (mapped == gid) - return NSS_STATUS_NOTFOUND; - - if (buflen < sizeof(char*) + 1) { - UNPROTECT_ERRNO; - *errnop = ERANGE; - return NSS_STATUS_TRYAGAIN; - } - - memzero(buffer, sizeof(char*)); - if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { - UNPROTECT_ERRNO; - *errnop = ERANGE; - return NSS_STATUS_TRYAGAIN; - } - - gr->gr_name = buffer + sizeof(char*); - gr->gr_gid = gid; - gr->gr_passwd = (char*) "*"; /* locked */ - gr->gr_mem = (char**) buffer; - - return NSS_STATUS_SUCCESS; - -fail: - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; + return NSS_STATUS_NOTFOUND; } diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index de46a8d46..43ab52163 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -10,6 +10,7 @@ #include "sd-bus.h" #include "bus-common-errors.h" +#include "bus-locator.h" #include "errno-util.h" #include "in-addr-util.h" #include "macro.h" @@ -142,13 +143,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( if (r < 0) goto fail; - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveHostname"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); if (r < 0) goto fail; @@ -322,13 +317,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( if (r < 0) goto fail; - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveHostname"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); if (r < 0) goto fail; @@ -514,13 +503,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( if (r < 0) goto fail; - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveAddress"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress"); if (r < 0) goto fail; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 6a38246f8..5dc5aacdf 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -8,7 +8,9 @@ #include "fd-util.h" #include "group-record-nss.h" #include "macro.h" +#include "nss-systemd.h" #include "nss-util.h" +#include "pthread-util.h" #include "signal-util.h" #include "strv.h" #include "user-util.h" @@ -277,10 +279,11 @@ static enum nss_status nss_systemd_endent(GetentData *p) { assert(p); - assert_se(pthread_mutex_lock(&p->mutex) == 0); + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL; + _l = pthread_mutex_lock_assert(&p->mutex); + p->iterator = userdb_iterator_free(p->iterator); p->by_membership = false; - assert_se(pthread_mutex_unlock(&p->mutex) == 0); return NSS_STATUS_SUCCESS; } @@ -294,45 +297,47 @@ enum nss_status _nss_systemd_endgrent(void) { } enum nss_status _nss_systemd_setpwent(int stayopen) { - enum nss_status ret; - PROTECT_ERRNO; BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - if (userdb_nss_compat_is_enabled() <= 0) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0); + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL; + int r; + + _l = pthread_mutex_lock_assert(&getpwent_data.mutex); getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator); getpwent_data.by_membership = false; - ret = userdb_all(nss_glue_userdb_flags(), &getpwent_data.iterator) < 0 ? - NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; - - assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0); - return ret; + /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records + * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete + * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback + * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the + * user database. */ + r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator); + return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } enum nss_status _nss_systemd_setgrent(int stayopen) { - enum nss_status ret; - PROTECT_ERRNO; BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - if (userdb_nss_compat_is_enabled() <= 0) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0); + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL; + int r; + + _l = pthread_mutex_lock_assert(&getgrent_data.mutex); getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator); getpwent_data.by_membership = false; - ret = groupdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator) < 0 ? - NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; - - assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0); - return ret; + /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */ + r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator); + return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } enum nss_status _nss_systemd_getpwent_r( @@ -341,7 +346,6 @@ enum nss_status _nss_systemd_getpwent_r( int *errnop) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; - enum nss_status ret; int r; PROTECT_ERRNO; @@ -350,49 +354,36 @@ enum nss_status _nss_systemd_getpwent_r( assert(result); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0); + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL; + + _l = pthread_mutex_lock_assert(&getpwent_data.mutex); if (!getpwent_data.iterator) { UNPROTECT_ERRNO; *errnop = EHOSTDOWN; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } r = userdb_iterator_get(getpwent_data.iterator, &ur); - if (r == -ESRCH) { - ret = NSS_STATUS_NOTFOUND; - goto finish; - } + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } r = nss_pack_user_record(ur, result, buffer, buflen); if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_TRYAGAIN; - goto finish; + return NSS_STATUS_TRYAGAIN; } - ret = NSS_STATUS_SUCCESS; - -finish: - assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0); - return ret; + return NSS_STATUS_SUCCESS; } enum nss_status _nss_systemd_getgrent_r( @@ -402,7 +393,6 @@ enum nss_status _nss_systemd_getgrent_r( _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; _cleanup_free_ char **members = NULL; - enum nss_status ret; int r; PROTECT_ERRNO; @@ -411,22 +401,17 @@ enum nss_status _nss_systemd_getgrent_r( assert(result); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) - return NSS_STATUS_UNAVAIL; + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; - assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0); + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL; + + _l = pthread_mutex_lock_assert(&getgrent_data.mutex); if (!getgrent_data.iterator) { UNPROTECT_ERRNO; *errnop = EHOSTDOWN; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } if (!getgrent_data.by_membership) { @@ -444,43 +429,37 @@ enum nss_status _nss_systemd_getgrent_r( if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } getgrent_data.by_membership = true; } else if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) { r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members); if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } } } if (getgrent_data.by_membership) { - _cleanup_close_ int lock_fd = -1; + _cleanup_(_nss_systemd_unblockp) bool blocked = false; for (;;) { _cleanup_free_ char *user_name = NULL, *group_name = NULL; r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name); - if (r == -ESRCH) { - ret = NSS_STATUS_NOTFOUND; - goto finish; - } + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_UNAVAIL; - goto finish; + return NSS_STATUS_UNAVAIL; } if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name)) @@ -489,17 +468,18 @@ enum nss_status _nss_systemd_getgrent_r( continue; /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */ - if (lock_fd < 0) { - lock_fd = userdb_nss_compat_disable(); - if (lock_fd < 0 && lock_fd != -EBUSY) { + if (!blocked) { + r = _nss_systemd_block(true); + if (r < 0) { UNPROTECT_ERRNO; - *errnop = -lock_fd; - ret = NSS_STATUS_UNAVAIL; - goto finish; + *errnop = -r; + return NSS_STATUS_UNAVAIL; } + + blocked = true; } - r = nss_group_record_by_name(group_name, &gr); + r = nss_group_record_by_name(group_name, false, &gr); if (r == -ESRCH) continue; if (r < 0) { @@ -511,8 +491,7 @@ enum nss_status _nss_systemd_getgrent_r( if (!members) { UNPROTECT_ERRNO; *errnop = ENOMEM; - ret = NSS_STATUS_TRYAGAIN; - goto finish; + return NSS_STATUS_TRYAGAIN; } /* Note that we currently generate one group entry per user that is part of a @@ -526,15 +505,10 @@ enum nss_status _nss_systemd_getgrent_r( if (r < 0) { UNPROTECT_ERRNO; *errnop = -r; - ret = NSS_STATUS_TRYAGAIN; - goto finish; + return NSS_STATUS_TRYAGAIN; } - ret = NSS_STATUS_SUCCESS; - -finish: - assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0); - return ret; + return NSS_STATUS_SUCCESS; } enum nss_status _nss_systemd_initgroups_dyn( @@ -566,13 +540,7 @@ enum nss_status _nss_systemd_initgroups_dyn( if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name)) return NSS_STATUS_NOTFOUND; - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - UNPROTECT_ERRNO; - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator); @@ -598,7 +566,7 @@ enum nss_status _nss_systemd_initgroups_dyn( /* The group might be defined via traditional NSS only, hence let's do a full look-up without * disabling NSS. This means we are operating recursively here. */ - r = groupdb_by_name(group_name, nss_glue_userdb_flags() & ~USERDB_AVOID_NSS, &g); + r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_AVOID_NSS) | USERDB_AVOID_SHADOW, &g); if (r == -ESRCH) continue; if (r < 0) { @@ -644,3 +612,29 @@ enum nss_status _nss_systemd_initgroups_dyn( return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND; } + +static thread_local unsigned _blocked = 0; + +_public_ int _nss_systemd_block(bool b) { + + /* This blocks recursively: it's blocked for as many times this function is called with `true` until + * it is called an equal time with `false`. */ + + if (b) { + if (_blocked >= UINT_MAX) + return -EOVERFLOW; + + _blocked++; + } else { + if (_blocked <= 0) + return -EOVERFLOW; + + _blocked--; + } + + return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */ +} + +_public_ bool _nss_systemd_is_blocked(void) { + return _blocked > 0; +} diff --git a/src/nss-systemd/nss-systemd.h b/src/nss-systemd/nss-systemd.h new file mode 100644 index 000000000..ffa75c12c --- /dev/null +++ b/src/nss-systemd/nss-systemd.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +int _nss_systemd_block(bool b); +bool _nss_systemd_is_blocked(void); + +/* For use with the _cleanup_() macro */ +static inline void _nss_systemd_unblockp(bool *b) { + if (*b) + assert_se(_nss_systemd_block(false) >= 0); +} diff --git a/src/nss-systemd/nss-systemd.sym b/src/nss-systemd/nss-systemd.sym index 77e1fbe93..f86d7643d 100644 --- a/src/nss-systemd/nss-systemd.sym +++ b/src/nss-systemd/nss-systemd.sym @@ -20,5 +20,9 @@ global: _nss_systemd_setgrent; _nss_systemd_getgrent_r; _nss_systemd_initgroups_dyn; + + /* These two are not used by glibc, but can be used by apps to explicitly disable nss-systemd for the calling thread. */ + _nss_systemd_block; + _nss_systemd_is_blocked; local: *; }; diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 58915c3d2..8e5b3eba6 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -3,6 +3,7 @@ #include "env-util.h" #include "fd-util.h" #include "group-record-nss.h" +#include "nss-systemd.h" #include "strv.h" #include "user-record.h" #include "userdb-glue.h" @@ -74,12 +75,7 @@ enum nss_status userdb_getpwnam( assert(pwd); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; r = userdb_by_name(name, nss_glue_userdb_flags(), &hr); @@ -112,12 +108,7 @@ enum nss_status userdb_getpwuid( assert(pwd); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr); @@ -214,12 +205,7 @@ enum nss_status userdb_getgrnam( assert(gr); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; r = groupdb_by_name(name, nss_glue_userdb_flags(), &g); @@ -235,7 +221,7 @@ enum nss_status userdb_getgrnam( } if (!g) { - _cleanup_close_ int lock_fd = -1; + _cleanup_(_nss_systemd_unblockp) bool blocked = false; if (strv_isempty(members)) return NSS_STATUS_NOTFOUND; @@ -245,13 +231,15 @@ enum nss_status userdb_getgrnam( * acquire it, so that we can extend it (that's because glibc's group merging feature will * merge groups only if both GID and name match and thus we need to have both first). It * sucks behaving recursively likely this, but it's apparently what everybody does. We break - * the recursion for ourselves via the userdb_nss_compat_disable() lock. */ + * the recursion for ourselves via the _nss_systemd_block_nss() lock. */ - lock_fd = userdb_nss_compat_disable(); - if (lock_fd < 0 && lock_fd != -EBUSY) - return lock_fd; + r = _nss_systemd_block(true); + if (r < 0) + return r; - r = nss_group_record_by_name(name, &g); + blocked = true; + + r = nss_group_record_by_name(name, false, &g); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -285,12 +273,7 @@ enum nss_status userdb_getgrgid( assert(gr); assert(errnop); - r = userdb_nss_compat_is_enabled(); - if (r < 0) { - *errnop = -r; - return NSS_STATUS_UNAVAIL; - } - if (!r) + if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g); @@ -300,20 +283,21 @@ enum nss_status userdb_getgrgid( } if (!g) { - _cleanup_close_ int lock_fd = -1; + _cleanup_(_nss_systemd_unblockp) bool blocked = false; /* So, quite possibly we have to extend an existing group record with additional members. But * to do this we need to know the group name first. The group didn't exist via non-NSS * queries though, hence let's try to acquire it here recursively via NSS. */ - lock_fd = userdb_nss_compat_disable(); - if (lock_fd < 0 && lock_fd != -EBUSY) - return lock_fd; + r = _nss_systemd_block(true); + if (r < 0) + return r; - r = nss_group_record_by_gid(gid, &g); + blocked = true; + + r = nss_group_record_by_gid(gid, false, &g); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; - if (r < 0) { *errnop = -r; return NSS_STATUS_UNAVAIL; @@ -329,7 +313,7 @@ enum nss_status userdb_getgrgid( return NSS_STATUS_UNAVAIL; } - /* If we acquired the record via NSS then there's no reason to respond unless we have to agument the + /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the * list of members of the group */ if (from_nss && strv_isempty(members)) return NSS_STATUS_NOTFOUND; diff --git a/src/partition/growfs.c b/src/partition/growfs.c index 7e2452a5d..98a7e4d31 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -79,7 +79,7 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ } #endif -static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) { +static int maybe_resize_underlying_device(const char *mountpath, dev_t main_devno) { _cleanup_free_ char *fstype = NULL, *devpath = NULL; dev_t devno; int r; @@ -213,7 +213,7 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target); - r = maybe_resize_slave_device(arg_target, devno); + r = maybe_resize_underlying_device(arg_target, devno); if (r < 0) return r; diff --git a/src/partition/makefs.c b/src/partition/makefs.c index df08a5fea..97f50c903 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -7,7 +7,9 @@ #include #include "alloc-util.h" +#include "blockdev-util.h" #include "dissect-image.h" +#include "fd-util.h" #include "main-func.h" #include "process-util.h" #include "signal-util.h" @@ -42,6 +44,7 @@ static int makefs(const char *type, const char *device) { static int run(int argc, char *argv[]) { _cleanup_free_ char *device = NULL, *type = NULL, *detected = NULL; + _cleanup_close_ int lock_fd = -1; struct stat st; int r; @@ -63,19 +66,22 @@ static int run(int argc, char *argv[]) { if (stat(device, &st) < 0) return log_error_errno(errno, "Failed to stat \"%s\": %m", device); - if (!S_ISBLK(st.st_mode)) + if (S_ISBLK(st.st_mode)) { + /* Lock the device so that udev doesn't interfere with our work */ + + lock_fd = lock_whole_block_device(st.st_rdev, LOCK_EX); + if (lock_fd < 0) + return log_error_errno(lock_fd, "Failed to lock whole block device of \"%s\": %m", device); + } else log_info("%s is not a block device.", device); r = probe_filesystem(device, &detected); + if (r == -EUCLEAN) + return log_error_errno(r, "Ambiguous results of probing for file system on \"%s\", refusing to proceed.", device); if (r < 0) - return log_warning_errno(r, - r == -EUCLEAN ? - "Cannot reliably determine probe \"%s\", refusing to proceed." : - "Failed to probe \"%s\": %m", - device); - + return log_error_errno(r, "Failed to probe \"%s\": %m", device); if (detected) { - log_info("%s is not empty (type %s), exiting", device, detected); + log_info("'%s' is not empty (contains file system of type %s), exiting.", device, detected); return 0; } diff --git a/src/partition/meson.build b/src/partition/meson.build index d0c111a47..3a75d5712 100644 --- a/src/partition/meson.build +++ b/src/partition/meson.build @@ -3,3 +3,5 @@ systemd_repart_sources = files(''' repart.c '''.split()) + +test_repart_sh = find_program('test-repart.sh') diff --git a/src/partition/repart.c b/src/partition/repart.c index 376ec3370..2e5f5d134 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -41,6 +41,7 @@ #include "pretty-print.h" #include "proc-cmdline.h" #include "sort-util.h" +#include "specifier.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -64,6 +65,7 @@ static enum { EMPTY_ALLOW, /* allow empty disks, create partition table if necessary */ EMPTY_REQUIRE, /* require an empty disk, create a partition table */ EMPTY_FORCE, /* make disk empty, erase everything, create a partition table always */ + EMPTY_CREATE, /* create disk as loopback file, create a partition table always */ } arg_empty = EMPTY_REFUSE; static bool arg_dry_run = true; @@ -76,6 +78,7 @@ static int arg_factory_reset = -1; static sd_id128_t arg_seed = SD_ID128_NULL; static bool arg_randomize = false; static int arg_pretty = -1; +static uint64_t arg_size = UINT64_MAX; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep); @@ -111,6 +114,10 @@ struct Partition { FreeArea *padding_area; FreeArea *allocated_to_area; + char *copy_blocks_path; + int copy_blocks_fd; + uint64_t copy_blocks_size; + LIST_FIELDS(Partition, partitions); }; @@ -171,6 +178,8 @@ static Partition *partition_new(void) { .padding_max = UINT64_MAX, .partno = UINT64_MAX, .offset = UINT64_MAX, + .copy_blocks_fd = -1, + .copy_blocks_size = UINT64_MAX, }; return p; @@ -189,6 +198,9 @@ static Partition* partition_free(Partition *p) { if (p->new_partition) fdisk_unref_partition(p->new_partition); + free(p->copy_blocks_path); + safe_close(p->copy_blocks_fd); + return mfree(p); } @@ -336,7 +348,11 @@ static uint64_t partition_min_size(const Partition *p) { } sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE; - return MAX(p->size_min == UINT64_MAX ? DEFAULT_MIN_SIZE : p->size_min, sz); + + if (p->copy_blocks_size != UINT64_MAX) + sz = MAX(p->copy_blocks_size, sz); + + return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz); } static uint64_t partition_max_size(const Partition *p) { @@ -391,7 +407,7 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) { uint64_t avail; /* Similar to free_area_available(), but takes into account that the required size and padding of the - * preceeding partition is honoured. */ + * preceding partition is honoured. */ avail = free_area_available(a); if (a->after) { @@ -558,7 +574,7 @@ static int context_grow_partitions_phase( LIST_FOREACH(partitions, p, context->partitions) { /* Look only at partitions associated with this free area, i.e. immediately - * preceeding it, or allocated into it */ + * preceding it, or allocated into it */ if (p->allocated_to_area != a && p->padding_area != a) continue; @@ -687,7 +703,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { break; } - /* We still have space left over? Donate to preceeding partition if we have one */ + /* We still have space left over? Donate to preceding partition if we have one */ if (span > 0 && a->after && !PARTITION_IS_FOREIGN(a->after)) { uint64_t m, xsz; @@ -702,7 +718,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { a->after->new_size = m; } - /* What? Even still some space left (maybe because there was no preceeding partition, or it had a + /* 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; @@ -731,7 +747,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { } } - /* Yuck, still noone? Then make it padding */ + /* Yuck, still no one? Then make it padding */ if (span > 0 && a->after) { assert(a->after->new_padding != UINT64_MAX); a->after->new_padding += span; @@ -860,33 +876,53 @@ static int config_parse_label( void *data, void *userdata) { + static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + {} + }; + _cleanup_free_ char16_t *recoded = NULL; + _cleanup_free_ char *resolved = NULL; char **label = data; int r; assert(rvalue); assert(label); - if (!utf8_is_valid(rvalue)) { + r = specifier_printf(rvalue, specifier_table, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to expand specifiers in Label=, ignoring: %s", rvalue); + return 0; + } + + if (!utf8_is_valid(resolved)) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Partition label not valid UTF-8, ignoring: %s", rvalue); return 0; } - recoded = utf8_to_utf16(rvalue, strlen(rvalue)); + recoded = utf8_to_utf16(resolved, strlen(resolved)); if (!recoded) return log_oom(); if (char16_strlen(recoded) > 36) { log_syntax(unit, LOG_WARNING, filename, line, 0, - "Partition label too long for GPT table, ignoring: %s", rvalue); + "Partition label too long for GPT table, ignoring: \"%s\" (from \"%s\")", + resolved, rvalue); return 0; } - r = free_and_strdup(label, rvalue); - if (r < 0) - return log_oom(); - + free_and_replace(*label, resolved); return 0; } @@ -916,7 +952,7 @@ static int config_parse_weight( } if (v > 1000U*1000U) { - log_syntax(unit, LOG_WARNING, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v); return 0; } @@ -945,7 +981,7 @@ static int config_parse_size4096( r = parse_size(rvalue, 1024, &parsed); if (r < 0) - return log_syntax(unit, LOG_WARNING, filename, line, r, + return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value: %s", rvalue); if (ltype > 0) @@ -964,21 +1000,28 @@ static int config_parse_size4096( static int partition_read_definition(Partition *p, const char *path) { ConfigTableItem table[] = { - { "Partition", "Type", config_parse_type, 0, &p->type_uuid }, - { "Partition", "Label", config_parse_label, 0, &p->new_label }, - { "Partition", "Priority", config_parse_int32, 0, &p->priority }, - { "Partition", "Weight", config_parse_weight, 0, &p->weight }, - { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight }, - { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min }, - { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max }, - { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min }, - { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max }, - { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset }, + { "Partition", "Type", config_parse_type, 0, &p->type_uuid }, + { "Partition", "Label", config_parse_label, 0, &p->new_label }, + { "Partition", "UUID", config_parse_id128, 0, &p->new_uuid }, + { "Partition", "Priority", config_parse_int32, 0, &p->priority }, + { "Partition", "Weight", config_parse_weight, 0, &p->weight }, + { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight }, + { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min }, + { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max }, + { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min }, + { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max }, + { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset }, + { "Partition", "CopyBlocks", config_parse_path, 0, &p->copy_blocks_path }, {} }; int r; - r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p); + r = config_parse(NULL, path, NULL, + "Partition\0", + config_item_table_lookup, table, + CONFIG_PARSE_WARN, + p, + NULL); if (r < 0) return r; @@ -1171,7 +1214,11 @@ static int disk_acquire_uuid(Context *context, sd_id128_t *ret) { return 0; } -static int context_load_partition_table(Context *context, const char *node) { +static int context_load_partition_table( + Context *context, + const char *node, + int *backing_fd) { + _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors; @@ -1183,14 +1230,31 @@ static int context_load_partition_table(Context *context, const char *node) { assert(context); assert(node); + assert(backing_fd); c = fdisk_new_context(); if (!c) return log_oom(); - r = fdisk_assign_device(c, node, arg_dry_run); + /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the + * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */ + if (*backing_fd < 0) + r = fdisk_assign_device(c, node, arg_dry_run); + else { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + xsprintf(procfs_path, "/proc/self/fd/%i", *backing_fd); + + r = fdisk_assign_device(c, procfs_path, arg_dry_run); + } if (r < 0) - return log_error_errno(r, "Failed to open device: %m"); + return log_error_errno(r, "Failed to open device '%s': %m", node); + + if (*backing_fd < 0) { + /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */ + *backing_fd = fcntl(fdisk_get_devfd(c), F_DUPFD_CLOEXEC, 3); + if (*backing_fd < 0) + return log_error_errno(errno, "Failed to duplicate fdisk fd: %m"); + } /* Tell udev not to interfere while we are processing the device */ if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0) @@ -1230,6 +1294,7 @@ static int context_load_partition_table(Context *context, const char *node) { break; case EMPTY_FORCE: + case EMPTY_CREATE: /* Always reinitiaize the disk, don't consider what there was on the disk before */ from_scratch = true; break; @@ -1602,7 +1667,7 @@ static int context_dump_partitions(Context *context, const char *node) { TABLE_UINT64, p->new_padding, TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); } if (sum_padding > 0 || sum_size > 0) { @@ -1625,7 +1690,7 @@ static int context_dump_partitions(Context *context, const char *node) { TABLE_EMPTY, TABLE_STRING, b); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); } r = table_print(t, stdout); @@ -1905,8 +1970,7 @@ static int context_discard_range(Context *context, uint64_t offset, uint64_t siz if (size <= 0) return 0; - fd = fdisk_get_devfd(context->fdisk_context); - assert(fd >= 0); + assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0); if (fstat(fd, &st) < 0) return -errno; @@ -2077,6 +2141,48 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) { return 0; } +static int context_copy_blocks(Context *context) { + Partition *p; + int fd = -1, r; + + assert(context); + + /* Copy in file systems on the block level */ + + LIST_FOREACH(partitions, p, context->partitions) { + char buf[FORMAT_BYTES_MAX]; + + if (p->copy_blocks_fd < 0) + continue; + + if (p->dropped) + continue; + + if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */ + continue; + + assert(p->new_size != UINT64_MAX); + assert(p->copy_blocks_size != UINT64_MAX); + assert(p->new_size >= p->copy_blocks_size); + + if (fd < 0) + assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0); + + if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1) + return log_error_errno(errno, "Failed to seek to partition offset: %m"); + + log_info("Copying in '%s' (%s) on block level into partition %" PRIu64 ".", p->copy_blocks_path, format_bytes(buf, sizeof(buf), p->copy_blocks_size), p->partno); + + r = copy_bytes_full(p->copy_blocks_fd, fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path); + + log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path); + } + + return 0; +} + static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) { struct { sd_id128_t type_uuid; @@ -2214,13 +2320,12 @@ static int context_acquire_partition_uuids_and_labels(Context *context) { assert(context); LIST_FOREACH(partitions, p, context->partitions) { - assert(sd_id128_is_null(p->new_uuid)); - /* Never touch foreign partitions */ if (PARTITION_IS_FOREIGN(p)) { p->new_uuid = p->current_uuid; if (p->current_label) { + free(p->new_label); p->new_label = strdup(p->current_label); if (!p->new_label) return log_oom(); @@ -2231,20 +2336,21 @@ static int context_acquire_partition_uuids_and_labels(Context *context) { if (!sd_id128_is_null(p->current_uuid)) p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */ - else { + else if (sd_id128_is_null(p->new_uuid)) { + /* Not explicitly set by user! */ r = partition_acquire_uuid(context, p, &p->new_uuid); if (r < 0) return r; } - if (p->new_label) /* Explicitly set by user? */ - continue; - if (!isempty(p->current_label)) { + free(p->new_label); p->new_label = strdup(p->current_label); /* never change initialized labels */ if (!p->new_label) return log_oom(); - } else { + } else if (!p->new_label) { + /* Not explicitly set by user! */ + r = partition_acquire_label(context, p, &p->new_label); if (r < 0) return r; @@ -2339,6 +2445,10 @@ static int context_write_partition_table( if (r < 0) return r; + r = context_copy_blocks(context); + if (r < 0) + return r; + LIST_FOREACH(partitions, p, context->partitions) { if (p->dropped) continue; @@ -2584,6 +2694,87 @@ static int context_can_factory_reset(Context *context) { return false; } +static int context_open_copy_block_paths(Context *context) { + Partition *p; + int r; + + assert(context); + + LIST_FOREACH(partitions, p, context->partitions) { + _cleanup_close_ int source_fd = -1; + uint64_t size; + struct stat st; + + assert(p->copy_blocks_fd < 0); + assert(p->copy_blocks_size == UINT64_MAX); + + if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */ + continue; + + if (!p->copy_blocks_path) + continue; + + source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (source_fd < 0) + return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path); + + if (fstat(source_fd, &st) < 0) + return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path); + + if (S_ISDIR(st.st_mode)) { + _cleanup_free_ char *bdev = NULL; + + /* If the file is a directory, automatically find the backing block device */ + + if (major(st.st_dev) != 0) + r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev); + else { + dev_t devt; + + /* Special support for btrfs */ + + r = btrfs_get_block_device_fd(source_fd, &devt); + if (r < 0) + return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path); + + r = device_path_make_major_minor(S_IFBLK, devt, &bdev); + } + if (r < 0) + return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path); + + safe_close(source_fd); + + source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (source_fd < 0) + return log_error_errno(errno, "Failed to open block device '%s': %m", bdev); + + if (fstat(source_fd, &st) < 0) + return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev); + + if (!S_ISBLK(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev); + } + + if (S_ISREG(st.st_mode)) + size = st.st_size; + else if (S_ISBLK(st.st_mode)) { + if (ioctl(source_fd, BLKGETSIZE64, &size) != 0) + return log_error_errno(errno, "Failed to determine size of block device to copy from: %m"); + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path); + + if (size <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path); + if (size % 512 != 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path); + + p->copy_blocks_fd = TAKE_FD(source_fd); + p->copy_blocks_size = size; + } + + return 0; +} + static int help(void) { _cleanup_free_ char *link = NULL; int r; @@ -2597,8 +2788,8 @@ static int help(void) { " -h --help Show this help\n" " --version Show package version\n" " --dry-run=BOOL Whether to run dry-run operation\n" - " --empty=MODE One of refuse, allow, require, force; controls how to\n" - " handle empty disks lacking partition table\n" + " --empty=MODE One of refuse, allow, require, force, create; controls\n" + " how to handle empty disks lacking partition tables\n" " --discard=BOOL Whether to discard backing blocks for new partitions\n" " --pretty=BOOL Whether to show pretty summary before executing operation\n" " --factory-reset=BOOL Whether to remove data partitions before recreating\n" @@ -2607,6 +2798,7 @@ static int help(void) { " --root=PATH Operate relative to root path\n" " --definitions=DIR Find partitions in specified directory\n" " --seed=UUID 128bit seed UUID to derive all UUIDs from\n" + " --size=BYTES Grow loopback file to specified size\n" "\nSee the %s for details.\n" , program_invocation_short_name , ansi_highlight(), ansi_normal() @@ -2629,6 +2821,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SEED, ARG_PRETTY, ARG_DEFINITIONS, + ARG_SIZE, }; static const struct option options[] = { @@ -2643,10 +2836,11 @@ static int parse_argv(int argc, char *argv[]) { { "seed", required_argument, NULL, ARG_SEED }, { "pretty", required_argument, NULL, ARG_PRETTY }, { "definitions", required_argument, NULL, ARG_DEFINITIONS }, + { "size", required_argument, NULL, ARG_SIZE }, {} }; - int c, r; + int c, r, dry_run = -1; assert(argc >= 0); assert(argv); @@ -2666,7 +2860,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse --dry-run= parameter: %s", optarg); - arg_dry_run = r; + dry_run = r; break; case ARG_EMPTY: @@ -2678,7 +2872,14 @@ static int parse_argv(int argc, char *argv[]) { arg_empty = EMPTY_REQUIRE; else if (streq(optarg, "force")) arg_empty = EMPTY_FORCE; - else + else if (streq(optarg, "create")) { + arg_empty = EMPTY_CREATE; + + if (dry_run < 0) + dry_run = false; /* Imply --dry-run=no if we create the loopback file + * anew. After all we cannot really break anyone's + * partition tables that way. */ + } else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse --empty= parameter: %s", optarg); break; @@ -2739,6 +2940,27 @@ static int parse_argv(int argc, char *argv[]) { return r; break; + case ARG_SIZE: { + uint64_t parsed, rounded; + + r = parse_size(optarg, 1024, &parsed); + if (r < 0) + return log_error_errno(r, "Failed to parse --size= parameter: %s", optarg); + + rounded = round_up_size(parsed, 4096); + if (rounded == 0) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too small, refusing."); + if (rounded == UINT64_MAX) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too large, refusing."); + + if (rounded != parsed) + log_warning("Specified size is not a multiple of 4096, rounding up automatically. (%" PRIu64 " → %" PRIu64 ")", + parsed, rounded); + + arg_size = rounded; + break; + } + case '?': return -EINVAL; @@ -2750,14 +2972,27 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected at most one argument, the path to the block device."); - if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE)) + if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Combination of --factory-reset=yes and --empty=force/--empty=require is invalid."); + "Combination of --factory-reset=yes and --empty=force/--empty=require/--empty=create is invalid."); if (arg_can_factory_reset) - arg_dry_run = true; + arg_dry_run = true; /* When --can-factory-reset is specified we don't make changes, hence + * non-dry-run mode makes no sense. Thus, imply dry run mode so that we + * open things strictly read-only. */ + else if (dry_run >= 0) + arg_dry_run = dry_run; + + if (arg_empty == EMPTY_CREATE && arg_size == UINT64_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "If --empty=create is specified, --size= must be specified, too."); arg_node = argc > optind ? argv[optind] : NULL; + + if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "A path to a device node or loopback file must be specified when --empty=force, --empty=require or --empty=create are used."); + return 1; } @@ -2824,12 +3059,16 @@ static int remove_efi_variable_factory_reset(void) { return 0; } -static int acquire_root_devno(const char *p, int mode, char **ret) { +static int acquire_root_devno(const char *p, int mode, char **ret, int *ret_fd) { _cleanup_close_ int fd = -1; struct stat st; - dev_t devno; + dev_t devno, fd_devno = (mode_t) -1; int r; + assert(p); + assert(ret); + assert(ret_fd); + fd = open(p, mode); if (fd < 0) return -errno; @@ -2845,23 +3084,23 @@ static int acquire_root_devno(const char *p, int mode, char **ret) { return log_oom(); *ret = s; + *ret_fd = TAKE_FD(fd); + return 0; } if (S_ISBLK(st.st_mode)) - devno = st.st_rdev; + fd_devno = devno = st.st_rdev; else if (S_ISDIR(st.st_mode)) { devno = st.st_dev; - - if (major(st.st_dev) == 0) { + if (major(devno) == 0) { r = btrfs_get_block_device_fd(fd, &devno); if (r == -ENOTTY) /* not btrfs */ return -ENODEV; if (r < 0) return r; } - } else return -ENOTBLK; @@ -2873,23 +3112,52 @@ static int acquire_root_devno(const char *p, int mode, char **ret) { /* From partition to whole disk containing it */ r = block_get_whole_disk(devno, &devno); if (r < 0) - log_debug_errno(r, "Failed to find whole disk block device for '%s', ingoring: %m", p); + log_debug_errno(r, "Failed to find whole disk block device for '%s', ignoring: %m", p); - return device_path_make_canonical(S_IFBLK, devno, ret); + r = device_path_make_canonical(S_IFBLK, devno, ret); + if (r < 0) + return log_debug_errno(r, "Failed to determine canonical path for '%s': %m", p); + + /* Only if we still lock at the same block device we can reuse the fd. Otherwise return an + * invalidated fd. */ + *ret_fd = fd_devno != (mode_t) -1 && fd_devno == devno ? TAKE_FD(fd) : -1; + return 0; } -static int find_root(char **ret) { +static int find_root(char **ret, int *ret_fd) { const char *t; int r; + assert(ret); + assert(ret_fd); + if (arg_node) { - r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret); + if (arg_empty == EMPTY_CREATE) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *s = NULL; + + s = strdup(arg_node); + if (!s) + return log_oom(); + + fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0777); + if (fd < 0) + return log_error_errno(errno, "Failed to create '%s': %m", arg_node); + + *ret = TAKE_PTR(s); + *ret_fd = TAKE_FD(fd); + return 0; + } + + r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret, ret_fd); if (r < 0) return log_error_errno(r, "Failed to determine backing device of %s: %m", arg_node); return 0; } + assert(IN_SET(arg_empty, EMPTY_REFUSE, EMPTY_ALLOW)); + /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device * (think: volatile setups) */ @@ -2907,7 +3175,7 @@ static int find_root(char **ret) { } else p = t; - r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret); + r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd); if (r < 0) { if (r != -ENODEV) return log_error_errno(r, "Failed to determine backing device of %s: %m", p); @@ -2918,9 +3186,83 @@ static int find_root(char **ret) { return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device."); } +static int resize_backing_fd(const char *node, int *fd) { + char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX]; + _cleanup_close_ int writable_fd = -1; + struct stat st; + int r; + + assert(node); + assert(fd); + + if (arg_size == UINT64_MAX) /* Nothing to do */ + return 0; + + if (*fd < 0) { + /* Open the file if we haven't opened it yet. Note that we open it read-only here, just to + * keep a reference to the file we can pass around. */ + *fd = open(node, O_RDONLY|O_CLOEXEC); + if (*fd < 0) + return log_error_errno(errno, "Failed to open '%s' in order to adjust size: %m", node); + } + + if (fstat(*fd, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", node); + + r = stat_verify_regular(&st); + if (r < 0) + return log_error_errno(r, "Specified path '%s' is not a regular file, cannot resize: %m", node); + + assert_se(format_bytes(buf1, sizeof(buf1), st.st_size)); + assert_se(format_bytes(buf2, sizeof(buf2), arg_size)); + + if ((uint64_t) st.st_size >= arg_size) { + log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)", node, buf1, buf2); + return 0; + } + + /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We + * reopen the file for that temporarily. We keep the writable fd only open for this operation though, + * as fdisk can't accept it anyway. */ + + writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC); + if (writable_fd < 0) + return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node); + + if (!arg_discard) { + if (fallocate(writable_fd, 0, 0, arg_size) < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return log_error_errno(errno, "Failed to grow '%s' from %s to %s by allocation: %m", + node, buf1, buf2); + + /* Fallback to truncation, if fallocate() is not supported. */ + log_debug("Backing file system does not support fallocate(), falling back to ftruncate()."); + } else { + if (st.st_size == 0) /* Likely regular file just created by us */ + log_info("Allocated %s for '%s'.", buf2, node); + else + log_info("File '%s' grown from %s to %s by allocation.", node, buf1, buf2); + + return 1; + } + } + + if (ftruncate(writable_fd, arg_size) < 0) + return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m", + node, buf1, buf2); + + if (st.st_size == 0) /* Likely regular file just created by us */ + log_info("Sized '%s' to %s.", node, buf2); + else + log_info("File '%s' grown from %s to %s by truncation.", node, buf1, buf2); + + return 1; +} + static int run(int argc, char *argv[]) { _cleanup_(context_freep) Context* context = NULL; _cleanup_free_ char *node = NULL; + _cleanup_close_ int backing_fd = -1; bool from_scratch; int r; @@ -2955,16 +3297,22 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - if (context->n_partitions <= 0 && arg_empty != EMPTY_FORCE) { + if (context->n_partitions <= 0 && arg_empty == EMPTY_REFUSE) { log_info("Didn't find any partition definition files, nothing to do."); return 0; } - r = find_root(&node); + r = find_root(&node, &backing_fd); if (r < 0) return r; - r = context_load_partition_table(context, node); + if (arg_size != UINT64_MAX) { + r = resize_backing_fd(node, &backing_fd); + if (r < 0) + return r; + } + + r = context_load_partition_table(context, node, &backing_fd); if (r == -EHWPOISON) return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't * really an error when called at boot. */ @@ -2993,7 +3341,7 @@ static int run(int argc, char *argv[]) { /* Reload the reduced partition table */ context_unload_partition_table(context); - r = context_load_partition_table(context, node); + r = context_load_partition_table(context, node, &backing_fd); if (r < 0) return r; } @@ -3007,6 +3355,11 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + /* Open all files to copy blocks from now, since we want to take their size into consideration */ + r = context_open_copy_block_paths(context); + if (r < 0) + return r; + /* First try to fit new partitions in, dropping by priority until it fits */ for (;;) { if (context_allocate_partitions(context)) diff --git a/src/partition/test-repart.sh b/src/partition/test-repart.sh new file mode 100755 index 000000000..bfb9bcb4c --- /dev/null +++ b/src/partition/test-repart.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -ex + +repart=$1 +test -x $repart + +D=$(mktemp --directory) +trap "rm -rf '$D'" EXIT INT QUIT PIPE +mkdir -p $D/definitions + +SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8 + +$repart $D/zzz --empty=create --size=1G --seed=$SEED + +sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty + +cmp $D/empty - <$D/definitions/root.conf <$D/definitions/home.conf <$D/definitions/swap.conf <$D/populated + +cmp $D/populated - <$D/definitions/swap.conf <$D/definitions/extra.conf <>$D/definitions/home.conf +echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>$D/definitions/home.conf + +$repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions + +sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated2 + +cmp $D/populated2 - <$D/populated3 + +cmp $D/populated3 - <$D/definitions/extra2.conf <$D/populated4 + +cmp $D/populated4 - <cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &data_fd, sizeof(int)); - mh.msg_controllen = CMSG_SPACE(sizeof(int)); iovec = IOVEC_MAKE_STRING(name); if (sendmsg(socket_fd, &mh, MSG_NOSIGNAL) < 0) @@ -169,10 +165,7 @@ static int recv_item( char **ret_name, int *ret_fd) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control; char buffer[PATH_MAX+2]; struct iovec iov = IOVEC_INIT(buffer, sizeof(buffer)-1); struct msghdr mh = { @@ -387,7 +380,7 @@ static int portable_extract_by_path( if (r < 0) return log_debug_errno(r, "Failed to create temporary directory: %m"); - r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); + r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); if (r == -ENOPKG) sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path); else if (r == -EADDRNOTAVAIL) @@ -702,16 +695,28 @@ static int install_chroot_dropin( if (!text) return -ENOMEM; - if (endswith(m->name, ".service")) + if (endswith(m->name, ".service")) { + const char *os_release_source; + + if (access("/etc/os-release", F_OK) < 0) { + if (errno != ENOENT) + return log_debug_errno(errno, "Failed to check if /etc/os-release exists: %m"); + + os_release_source = "/usr/lib/os-release"; + } else + os_release_source = "/etc/os-release"; + if (!strextend(&text, "\n" "[Service]\n", IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=", image_path, "\n" "Environment=PORTABLE=", basename(image_path), "\n" + "BindReadOnlyPaths=", os_release_source, ":/run/host/os-release\n" "LogExtraFields=PORTABLE=", basename(image_path), "\n", NULL)) return -ENOMEM; + } r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); if (r < 0) @@ -1092,10 +1097,9 @@ static int test_chroot_dropin( return log_debug_errno(errno, "Failed to open %s/%s: %m", where, p); } - r = fdopen_unlocked(fd, "r", &f); + r = take_fdopen_unlocked(&fd, "r", &f); if (r < 0) return log_debug_errno(r, "Failed to convert file handle: %m"); - TAKE_FD(fd); r = read_line(f, LONG_LINE_MAX, &line); if (r < 0) @@ -1133,7 +1137,7 @@ int portable_detach( sd_bus_error *error) { _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_set_free_free_ Set *unit_files = NULL, *markers = NULL; + _cleanup_set_free_ Set *unit_files = NULL, *markers = NULL; _cleanup_closedir_ DIR *d = NULL; const char *where, *item; Iterator iterator; @@ -1157,14 +1161,6 @@ int portable_detach( return log_debug_errno(errno, "Failed to open '%s' directory: %m", where); } - unit_files = set_new(&string_hash_ops); - if (!unit_files) - return -ENOMEM; - - markers = set_new(&path_hash_ops); - if (!markers) - return -ENOMEM; - FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where)) { _cleanup_free_ char *marker = NULL; UnitFileState state; @@ -1173,7 +1169,7 @@ int portable_detach( continue; /* Filter out duplicates */ - if (set_get(unit_files, de->d_name)) + if (set_contains(unit_files, de->d_name)) continue; dirent_ensure_type(d, de); @@ -1198,22 +1194,16 @@ int portable_detach( if (r > 0) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active, can't detach.", de->d_name); - r = set_put_strdup(unit_files, de->d_name); + r = set_put_strdup(&unit_files, de->d_name); if (r < 0) return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name); if (path_is_absolute(marker) && !image_in_search_path(IMAGE_PORTABLE, marker)) { - r = set_ensure_allocated(&markers, &path_hash_ops); + r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(marker)); if (r < 0) return r; - - r = set_put(markers, marker); - if (r >= 0) - marker = NULL; - else if (r != -EEXIST) - return r; } } @@ -1311,7 +1301,7 @@ static int portable_get_state_internal( _cleanup_(lookup_paths_free) LookupPaths paths = {}; bool found_enabled = false, found_running = false; - _cleanup_set_free_free_ Set *unit_files = NULL; + _cleanup_set_free_ Set *unit_files = NULL; _cleanup_closedir_ DIR *d = NULL; const char *where; struct dirent *de; @@ -1337,10 +1327,6 @@ static int portable_get_state_internal( return log_debug_errno(errno, "Failed to open '%s' directory: %m", where); } - unit_files = set_new(&string_hash_ops); - if (!unit_files) - return -ENOMEM; - FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where)) { UnitFileState state; @@ -1348,7 +1334,7 @@ static int portable_get_state_internal( continue; /* Filter out duplicates */ - if (set_get(unit_files, de->d_name)) + if (set_contains(unit_files, de->d_name)) continue; dirent_ensure_type(d, de); @@ -1373,7 +1359,7 @@ static int portable_get_state_internal( if (r > 0) found_running = true; - r = set_put_strdup(unit_files, de->d_name); + r = set_put_strdup(&unit_files, de->d_name); if (r < 0) return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name); } diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index bf5badd69..aa6369864 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -7,8 +7,8 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-locator.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "bus-wait-for-jobs.h" #include "def.h" #include "dirent-util.h" @@ -239,13 +239,7 @@ static int inspect_image(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "GetImageMetadata"); + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata"); if (r < 0) return bus_log_create_error(r); @@ -554,13 +548,7 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) { if (r < 0) return log_error_errno(r, "Could not watch jobs: %m"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "GetImageMetadata"); + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata"); if (r < 0) return bus_log_create_error(r); @@ -643,13 +631,7 @@ static int attach_image(int argc, char *argv[], void *userdata) { (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "AttachImage"); + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "AttachImage"); if (r < 0) return bus_log_create_error(r); @@ -697,15 +679,7 @@ static int detach_image(int argc, char *argv[], void *userdata) { (void) maybe_stop_disable(bus, image, argv); - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "DetachImage", - &error, - &reply, - "sb", image, arg_runtime); + r = bus_call_method(bus, bus_portable_mgr, "DetachImage", &error, &reply, "sb", image, arg_runtime); if (r < 0) return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r)); @@ -726,15 +700,7 @@ static int list_images(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "ListImages", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_portable_mgr, "ListImages", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r)); @@ -778,13 +744,13 @@ static int list_images(int argc, char *argv[], void *userdata) { if (table_get_rows(table) > 1) { r = table_set_sort(table, (size_t) 0, (size_t) -1); if (r < 0) - return log_error_errno(r, "Failed to sort table: %m"); + return table_log_sort_error(r); table_set_header(table, arg_legend); r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } if (arg_legend) { @@ -811,13 +777,7 @@ static int remove_image(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; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "RemoveImage"); + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "RemoveImage"); if (r < 0) return bus_log_create_error(r); @@ -851,15 +811,7 @@ static int read_only_image(int argc, char *argv[], void *userdata) { (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "MarkImageReadOnly", - &error, - NULL, - "sb", argv[1], b); + r = bus_call_method(bus, bus_portable_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b); if (r < 0) return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r)); @@ -888,26 +840,10 @@ static int set_limit(int argc, char *argv[], void *userdata) { if (argc > 2) /* With two arguments changes the quota limit of the specified image */ - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "SetImageLimit", - &error, - NULL, - "st", argv[1], limit); + r = bus_call_method(bus, bus_portable_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit); else /* With one argument changes the pool quota limit */ - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "SetPoolLimit", - &error, - NULL, - "t", limit); + r = bus_call_method(bus, bus_portable_mgr, "SetPoolLimit", &error, NULL, "t", limit); if (r < 0) return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r)); @@ -931,15 +867,7 @@ static int is_image_attached(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "GetImageState", - &error, - &reply, - "s", image); + r = bus_call_method(bus, bus_portable_mgr, "GetImageState", &error, &reply, "s", image); if (r < 0) return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r)); @@ -964,14 +892,7 @@ static int dump_profiles(void) { if (r < 0) return r; - r = sd_bus_get_property_strv( - bus, - "org.freedesktop.portable1", - "/org/freedesktop/portable1", - "org.freedesktop.portable1.Manager", - "Profiles", - &error, - &l); + r = bus_get_property_strv(bus, bus_portable_mgr, "Profiles", &error, &l); if (r < 0) return log_error_errno(r, "Failed to acquire list of profiles: %s", bus_error_message(&error, r)); @@ -1198,9 +1119,7 @@ static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 2bd1c495e..964660149 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" #include "bus-label.h" #include "bus-polkit.h" #include "bus-util.h" @@ -79,12 +80,10 @@ static int append_fd(sd_bus_message *m, PortableMetadata *d) { assert(d); assert(d->fd >= 0); - f = fdopen(d->fd, "r"); + f = take_fdopen(&d->fd, "r"); if (!f) return -errno; - d->fd = -1; - r = read_full_stream(f, &buf, &n); if (r < 0) return r; diff --git a/src/portable/portabled.c b/src/portable/portabled.c index 75b76926e..265f7a744 100644 --- a/src/portable/portabled.c +++ b/src/portable/portabled.c @@ -7,6 +7,7 @@ #include "sd-daemon.h" #include "alloc-util.h" +#include "bus-log-control-api.h" #include "bus-polkit.h" #include "def.h" #include "main-func.h" @@ -84,6 +85,10 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add image enumerator: %m"); + r = bus_log_control_api_register(m->bus); + if (r < 0) + return r; + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.portable1", 0, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to request name: %m"); diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c index 5c812b5d5..9b888a2ba 100644 --- a/src/pstore/pstore.c +++ b/src/pstore/pstore.c @@ -78,11 +78,14 @@ static int parse_config(void) { {} }; - return config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf", - CONF_PATHS_NULSTR("systemd/pstore.conf.d"), - "PStore\0", - config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + return config_parse_many_nulstr( + PKGSYSCONFDIR "/pstore.conf", + CONF_PATHS_NULSTR("systemd/pstore.conf.d"), + "PStore\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); } /* File list handling - PStoreEntry is the struct and @@ -98,8 +101,8 @@ typedef struct PStoreEntry { typedef struct PStoreList { PStoreEntry *entries; + size_t n_allocated; size_t n_entries; - size_t n_entries_allocated; } PStoreList; static void pstore_entries_reset(PStoreList *list) { @@ -109,8 +112,7 @@ static void pstore_entries_reset(PStoreList *list) { list->n_entries = 0; } -static int compare_pstore_entries(const void *_a, const void *_b) { - PStoreEntry *a = (PStoreEntry *)_a, *b = (PStoreEntry *)_b; +static int compare_pstore_entries(const PStoreEntry *a, const PStoreEntry *b) { return strcmp(a->dirent.d_name, b->dirent.d_name); } @@ -346,7 +348,7 @@ static int list_files(PStoreList *list, const char *sourcepath) { continue; } - if (!GREEDY_REALLOC(list->entries, list->n_entries_allocated, list->n_entries + 1)) + if (!GREEDY_REALLOC(list->entries, list->n_allocated, list->n_entries + 1)) return log_oom(); list->entries[list->n_entries++] = (PStoreEntry) { @@ -391,7 +393,7 @@ static int run(int argc, char *argv[]) { /* Handle each pstore file */ /* Sort files lexigraphically ascending, generally needed by all */ - qsort_safe(list.entries, list.n_entries, sizeof(PStoreEntry), compare_pstore_entries); + typesafe_qsort(list.entries, list.n_entries, compare_pstore_entries); /* Process known file types */ process_dmesg_files(&list); diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c index 596bff98f..63ad97751 100644 --- a/src/random-seed/random-seed.c +++ b/src/random-seed/random-seed.c @@ -236,24 +236,10 @@ static int run(int argc, char *argv[]) { } } - if (IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED)) { - _cleanup_free_ struct rand_pool_info *info = NULL; - - info = malloc(offsetof(struct rand_pool_info, buf) + k); - if (!info) - return log_oom(); - - info->entropy_count = k * 8; - info->buf_size = k; - memcpy(info->buf, buf, k); - - if (ioctl(random_fd, RNDADDENTROPY, info) < 0) - return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m"); - } else { - r = loop_write(random_fd, buf, (size_t) k, false); - if (r < 0) - log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); - } + r = random_write_entropy(random_fd, buf, k, + IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED)); + if (r < 0) + log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); } } @@ -305,7 +291,7 @@ static int run(int argc, char *argv[]) { * entropy later on. Let's keep that in mind by setting an extended attribute. on the file */ if (getrandom_worked) if (fsetxattr(seed_fd, "user.random-seed-creditable", "1", 1, 0) < 0) - log_full_errno(IN_SET(errno, ENOSYS, EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, errno, + log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to mark seed file as creditable, ignoring: %m"); } diff --git a/src/resolve/meson.build b/src/resolve/meson.build index c4d8d4e5d..92b67b633 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -64,8 +64,6 @@ systemd_resolved_sources = files(''' resolved-etc-hosts.h resolved-etc-hosts.c resolved-dnstls.h - resolved-util.c - resolved-util.h '''.split()) resolvectl_sources = files(''' @@ -230,10 +228,4 @@ tests += [ [], [], 'ENABLE_RESOLVE', 'manual'], - - [['src/resolve/test-resolved-util.c', - 'src/resolve/resolved-util.c', - 'src/resolve/resolved-util.h'], - [], - []], ] diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index f20e8c44b..3072b984e 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -11,7 +11,9 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-message-util.h" #include "dns-domain.h" #include "escape.h" #include "format-table.h" @@ -28,6 +30,7 @@ #include "resolved-def.h" #include "resolved-dns-packet.h" #include "socket-netlink.h" +#include "sort-util.h" #include "stdio-util.h" #include "string-table.h" #include "strv.h" @@ -79,6 +82,21 @@ typedef enum StatusMode { STATUS_NTA, } StatusMode; +typedef struct InterfaceInfo { + int index; + const char *name; +} InterfaceInfo; + +static int interface_info_compare(const InterfaceInfo *a, const InterfaceInfo *b) { + int r; + + r = CMP(a->index, b->index); + if (r != 0) + return r; + + return strcmp_ptr(a->name, b->name); +} + int ifname_mangle(const char *s) { _cleanup_free_ char *iface = NULL; const char *dot; @@ -169,13 +187,7 @@ static int resolve_host(sd_bus *bus, const char *name) { log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname); - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveHostname"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); if (r < 0) return bus_log_create_error(r); @@ -198,34 +210,29 @@ static int resolve_host(sd_bus *bus, const char *name) { while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { _cleanup_free_ char *pretty = NULL; int ifindex, family, k; - const void *a; - size_t sz; + union in_addr_union a; assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(reply, "ii", &ifindex, &family); + r = sd_bus_message_read(reply, "i", &ifindex); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - return bus_log_parse_error(r); + sd_bus_error_free(&error); + r = bus_message_read_in_addr_auto(reply, &error, &family, &a); + if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) + return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r)); r = sd_bus_message_exit_container(reply); if (r < 0) return bus_log_parse_error(r); - if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown"); + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) { + log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r)); continue; } - if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); - return -EINVAL; - } - - r = in_addr_ifindex_to_string(family, a, ifindex, &pretty); + r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty); if (r < 0) return log_error_errno(r, "Failed to print address for %s: %m", name); @@ -286,13 +293,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a log_debug("Resolving %s.", pretty); - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveAddress"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress"); if (r < 0) return bus_log_create_error(r); @@ -421,13 +422,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(arg_ifname) ? "*" : arg_ifname); - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveRecord"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveRecord"); if (r < 0) return bus_log_create_error(r); @@ -689,13 +684,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons else log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname); - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveService"); + r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveService"); if (r < 0) return bus_log_create_error(r); @@ -747,33 +736,29 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { _cleanup_free_ char *pretty = NULL; int ifindex, family, k; - const void *a; + union in_addr_union a;; assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(reply, "ii", &ifindex, &family); + r = sd_bus_message_read(reply, "i", &ifindex); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - return bus_log_parse_error(r); + sd_bus_error_free(&error); + r = bus_message_read_in_addr_auto(reply, &error, &family, &a); + if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) + return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r)); r = sd_bus_message_exit_container(reply); if (r < 0) return bus_log_parse_error(r); - if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown"); + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) { + log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r)); continue; } - if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); - return -EINVAL; - } - - r = in_addr_ifindex_to_string(family, a, ifindex, &pretty); + r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty); if (r < 0) return log_error_errno(r, "Failed to print address for %s: %m", name); @@ -994,14 +979,7 @@ static int show_statistics(int argc, char **argv, void *userdata) { assert(bus); - r = sd_bus_get_property_trivial(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "DNSSECSupported", - &error, - 'b', - &dnssec_supported); + r = bus_get_property_trivial(bus, bus_resolve_mgr, "DNSSECSupported", &error, 'b', &dnssec_supported); if (r < 0) return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r)); @@ -1010,14 +988,7 @@ static int show_statistics(int argc, char **argv, void *userdata) { yes_no(dnssec_supported), ansi_normal()); - r = sd_bus_get_property(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "TransactionStatistics", - &error, - &reply, - "(tt)"); + r = bus_get_property(bus, bus_resolve_mgr, "TransactionStatistics", &error, &reply, "(tt)"); if (r < 0) return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r)); @@ -1029,14 +1000,7 @@ static int show_statistics(int argc, char **argv, void *userdata) { reply = sd_bus_message_unref(reply); - r = sd_bus_get_property(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "CacheStatistics", - &error, - &reply, - "(ttt)"); + r = bus_get_property(bus, bus_resolve_mgr, "CacheStatistics", &error, &reply, "(ttt)"); if (r < 0) return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r)); @@ -1049,14 +1013,7 @@ static int show_statistics(int argc, char **argv, void *userdata) { reply = sd_bus_message_unref(reply); - r = sd_bus_get_property(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "DNSSECStatistics", - &error, - &reply, - "(tttt)"); + r = bus_get_property(bus, bus_resolve_mgr, "DNSSECStatistics", &error, &reply, "(tttt)"); if (r < 0) return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r)); @@ -1114,7 +1071,7 @@ static int show_statistics(int argc, char **argv, void *userdata) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); return 0; } @@ -1124,14 +1081,7 @@ static int reset_statistics(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; int r; - r = sd_bus_call_method(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResetStatistics", - &error, - NULL, - NULL); + r = bus_call_method(bus, bus_resolve_mgr, "ResetStatistics", &error, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r)); @@ -1143,14 +1093,7 @@ static int flush_caches(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; int r; - r = sd_bus_call_method(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "FlushCaches", - &error, - NULL, - NULL); + r = bus_call_method(bus, bus_resolve_mgr, "FlushCaches", &error, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r)); @@ -1162,30 +1105,25 @@ static int reset_server_features(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; int r; - r = sd_bus_call_method(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResetServerFeatures", - &error, - NULL, - NULL); + r = bus_call_method(bus, bus_resolve_mgr, "ResetServerFeatures", &error, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r)); return 0; } -static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) { +static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, bool extended, char **ret) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *pretty = NULL; - int ifindex, family, r; - const void *a; - size_t sz; + int ifindex, family, r, k; + union in_addr_union a; + const char *name = NULL; + uint16_t port = 0; assert(m); assert(ret); - r = sd_bus_message_enter_container(m, 'r', with_ifindex ? "iiay" : "iay"); + r = sd_bus_message_enter_container(m, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay")); if (r <= 0) return r; @@ -1195,39 +1133,37 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) return r; } - r = sd_bus_message_read(m, "i", &family); - if (r < 0) - return r; + k = bus_message_read_in_addr_auto(m, &error, &family, &a); + if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) + return k; - r = sd_bus_message_read_array(m, 'y', &a, &sz); - if (r < 0) - return r; + if (extended) { + r = sd_bus_message_read(m, "q", &port); + if (r < 0) + return r; + + r = sd_bus_message_read(m, "s", &name); + if (r < 0) + return r; + } r = sd_bus_message_exit_container(m); if (r < 0) return r; + if (k < 0) { + log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k)); + *ret = NULL; + return 1; + } + if (with_ifindex && ifindex != 0) { /* only show the global ones here */ *ret = NULL; return 1; } - if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("Unexpected family, ignoring: %i", family); - - *ret = NULL; - return 1; - } - - if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_debug("Address size mismatch, ignoring."); - - *ret = NULL; - return 1; - } - - r = in_addr_to_string(family, a, &pretty); + r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty); if (r < 0) return r; @@ -1236,7 +1172,7 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) return 1; } -static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { +static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) { char ***l = userdata; int r; @@ -1245,14 +1181,14 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message assert(m); assert(l); - r = sd_bus_message_enter_container(m, 'a', "(iay)"); + r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)"); if (r < 0) return r; for (;;) { _cleanup_free_ char *pretty = NULL; - r = read_dns_server_one(m, false, &pretty); + r = read_dns_server_one(m, false, extended, &pretty); if (r < 0) return r; if (r == 0) @@ -1273,11 +1209,26 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message return 0; } +static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_link_dns_servers_internal(bus, member, m, error, userdata, false); +} + +static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_link_dns_servers_internal(bus, member, m, error, userdata, true); +} + static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { assert(m); assert(userdata); - return read_dns_server_one(m, false, userdata); + return read_dns_server_one(m, false, false, userdata); +} + +static int map_link_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + assert(m); + assert(userdata); + + return read_dns_server_one(m, false, true, userdata); } static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) { @@ -1371,7 +1322,9 @@ struct link_info { const char *dns_over_tls; const char *dnssec; char *current_dns; + char *current_dns_ex; char **dns; + char **dns_ex; char **domains; char **ntas; bool dnssec_supported; @@ -1380,7 +1333,9 @@ struct link_info { static void link_info_clear(struct link_info *p) { free(p->current_dns); + free(p->current_dns_ex); strv_free(p->dns); + strv_free(p->dns_ex); strv_free(p->domains); strv_free(p->ntas); } @@ -1402,17 +1357,19 @@ static int dump_list(Table *table, const char *prefix, char * const *l) { static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) { static const struct bus_properties_map property_map[] = { - { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) }, - { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) }, - { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) }, - { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) }, - { "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) }, - { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) }, - { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) }, - { "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) }, - { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) }, - { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) }, - { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) }, + { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) }, + { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) }, + { "DNSEx", "a(iayqs)", map_link_dns_servers_ex, offsetof(struct link_info, dns_ex) }, + { "CurrentDNSServer", "(iay)", map_link_current_dns_server, offsetof(struct link_info, current_dns) }, + { "CurrentDNSServerEx", "(iayqs)", map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex) }, + { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) }, + { "DefaultRoute", "b", NULL, offsetof(struct link_info, default_route) }, + { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) }, + { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) }, + { "DNSOverTLS", "s", NULL, offsetof(struct link_info, dns_over_tls) }, + { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) }, + { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) }, + { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) }, {} }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1452,7 +1409,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode (void) pager_open(arg_pager_flags); if (mode == STATUS_DNS) - return status_print_strv_ifindex(ifindex, name, link_info.dns); + return status_print_strv_ifindex(ifindex, name, link_info.dns_ex ?: link_info.dns); if (mode == STATUS_DOMAIN) return status_print_strv_ifindex(ifindex, name, link_info.domains); @@ -1560,12 +1517,12 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode if (link_info.current_dns) { r = table_add_many(table, TABLE_STRING, "Current DNS Server:", - TABLE_STRING, link_info.current_dns); + TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns); if (r < 0) return table_log_add_error(r); } - r = dump_list(table, "DNS Servers:", link_info.dns); + r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns); if (r < 0) return r; @@ -1573,13 +1530,9 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode if (r < 0) return r; - r = dump_list(table, "DNSSEC NTA:", link_info.ntas); - if (r < 0) - return r; - r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); if (empty_line) *empty_line = true; @@ -1587,7 +1540,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode return 0; } -static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { +static int map_global_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) { char ***l = userdata; int r; @@ -1596,14 +1549,14 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag assert(m); assert(l); - r = sd_bus_message_enter_container(m, 'a', "(iiay)"); + r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)"); if (r < 0) return r; for (;;) { _cleanup_free_ char *pretty = NULL; - r = read_dns_server_one(m, true, &pretty); + r = read_dns_server_one(m, true, extended, &pretty); if (r < 0) return r; if (r == 0) @@ -1624,11 +1577,26 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag return 0; } +static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_global_dns_servers_internal(bus, member, m, error, userdata, false); +} + +static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + return map_global_dns_servers_internal(bus, member, m, error, userdata, true); +} + static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { assert(m); assert(userdata); - return read_dns_server_one(m, true, userdata); + return read_dns_server_one(m, true, false, userdata); +} + +static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + assert(m); + assert(userdata); + + return read_dns_server_one(m, true, true, userdata); } static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { @@ -1683,8 +1651,11 @@ static int status_print_strv_global(char **p) { struct global_info { char *current_dns; + char *current_dns_ex; char **dns; + char **dns_ex; char **fallback_dns; + char **fallback_dns_ex; char **domains; char **ntas; const char *llmnr; @@ -1696,24 +1667,30 @@ struct global_info { static void global_info_clear(struct global_info *p) { free(p->current_dns); + free(p->current_dns_ex); strv_free(p->dns); + strv_free(p->dns_ex); strv_free(p->fallback_dns); + strv_free(p->fallback_dns_ex); strv_free(p->domains); strv_free(p->ntas); } static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { static const struct bus_properties_map property_map[] = { - { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) }, - { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) }, - { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) }, - { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) }, - { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) }, - { "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) }, - { "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) }, - { "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) }, - { "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) }, - { "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) }, + { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) }, + { "DNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, dns_ex) }, + { "FallbackDNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, fallback_dns) }, + { "FallbackDNSEx", "a(iiayqs)", map_global_dns_servers_ex, offsetof(struct global_info, fallback_dns_ex) }, + { "CurrentDNSServer", "(iiay)", map_global_current_dns_server, offsetof(struct global_info, current_dns) }, + { "CurrentDNSServerEx", "(iiayqs)", map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex) }, + { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) }, + { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) }, + { "LLMNR", "s", NULL, offsetof(struct global_info, llmnr) }, + { "MulticastDNS", "s", NULL, offsetof(struct global_info, mdns) }, + { "DNSOverTLS", "s", NULL, offsetof(struct global_info, dns_over_tls) }, + { "DNSSEC", "s", NULL, offsetof(struct global_info, dnssec) }, + { "DNSSECSupported", "b", NULL, offsetof(struct global_info, dnssec_supported) }, {} }; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1739,7 +1716,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { (void) pager_open(arg_pager_flags); if (mode == STATUS_DNS) - return status_print_strv_global(global_info.dns); + return status_print_strv_global(global_info.dns_ex ?: global_info.dns); if (mode == STATUS_DOMAIN) return status_print_strv_global(global_info.domains); @@ -1801,16 +1778,16 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { if (global_info.current_dns) { r = table_add_many(table, TABLE_STRING, "Current DNS Server:", - TABLE_STRING, global_info.current_dns); + TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns); if (r < 0) return table_log_add_error(r); } - r = dump_list(table, "DNS Servers:", global_info.dns); + r = dump_list(table, "DNS Servers:", global_info.dns_ex ?: global_info.dns); if (r < 0) return r; - r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns); + r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns_ex ?: global_info.fallback_dns); if (r < 0) return r; @@ -1818,14 +1795,9 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { if (r < 0) return r; - strv_sort(global_info.ntas); - r = dump_list(table, "DNSSEC NTA:", global_info.ntas); - if (r < 0) - return r; - r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to print table: %m"); + return table_log_print_error(r); *empty_line = true; @@ -1835,7 +1807,6 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) { static int status_all(sd_bus *bus, StatusMode mode) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - sd_netlink_message *i; bool empty_line = false; int r; @@ -1861,31 +1832,43 @@ static int status_all(sd_bus *bus, StatusMode mode) { if (r < 0) return log_error_errno(r, "Failed to enumerate links: %m"); - r = 0; - for (i = reply; i; i = sd_netlink_message_next(i)) { + _cleanup_free_ InterfaceInfo *infos = NULL; + size_t n_allocated = 0, n_infos = 0; + + for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) { const char *name; - int ifindex, q; + int ifindex; uint16_t type; - q = sd_netlink_message_get_type(i, &type); - if (q < 0) - return rtnl_log_parse_error(q); + r = sd_netlink_message_get_type(i, &type); + if (r < 0) + return rtnl_log_parse_error(r); if (type != RTM_NEWLINK) continue; - q = sd_rtnl_message_link_get_ifindex(i, &ifindex); - if (q < 0) - return rtnl_log_parse_error(q); + r = sd_rtnl_message_link_get_ifindex(i, &ifindex); + if (r < 0) + return rtnl_log_parse_error(r); if (ifindex == LOOPBACK_IFINDEX) continue; - q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); - if (q < 0) - return rtnl_log_parse_error(q); + r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); + if (r < 0) + return rtnl_log_parse_error(r); - q = status_ifindex(bus, ifindex, name, mode, &empty_line); + if (!GREEDY_REALLOC(infos, n_allocated, n_infos + 1)) + return log_oom(); + + infos[n_infos++] = (InterfaceInfo) { ifindex, name }; + } + + typesafe_qsort(infos, n_infos, interface_info_compare); + + r = 0; + for (size_t i = 0; i < n_infos; i++) { + int q = status_ifindex(bus, infos[i].index, infos[i].name, mode, &empty_line); if (q < 0 && r >= 0) r = q; } @@ -1921,18 +1904,12 @@ static int verb_status(int argc, char **argv, void *userdata) { return r; } -static int call_dns(sd_bus *bus, char **dns, const char *destination, const char *path, const char *interface, sd_bus_error *error) { +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 = sd_bus_message_new_method_call( - bus, - &req, - destination, - path, - interface, - "SetLinkDNS"); + r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS"); if (r < 0) return bus_log_create_error(r); @@ -1940,7 +1917,7 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(req, 'a', "(iay)"); + r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)"); if (r < 0) return bus_log_create_error(r); @@ -1948,13 +1925,19 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char * empty list, which will clear the list of domains for an interface. */ if (!strv_equal(dns, STRV_MAKE(""))) STRV_FOREACH(p, dns) { + _cleanup_free_ char *name = NULL; struct in_addr_data data; + uint16_t port; + int ifindex; - r = in_addr_from_string_auto(*p, &data.family, &data.address); + r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name); if (r < 0) return log_error_errno(r, "Failed to parse DNS server address: %s", *p); - r = sd_bus_message_open_container(req, 'r', "iay"); + if (ifindex != 0 && ifindex != arg_ifindex) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex); + + r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay"); if (r < 0) return bus_log_create_error(r); @@ -1966,6 +1949,16 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char if (r < 0) return bus_log_create_error(r); + if (extended) { + r = sd_bus_message_append(req, "q", port); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "s", name); + if (r < 0) + return bus_log_create_error(r); + } + r = sd_bus_message_close_container(req); if (r < 0) return bus_log_create_error(r); @@ -1975,7 +1968,12 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char if (r < 0) return bus_log_create_error(r); - return sd_bus_call(bus, req, 0, error, NULL); + r = sd_bus_call(bus, req, 0, error, NULL); + if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + sd_bus_error_free(error); + return call_dns(bus, dns, locator, error, false); + } + return r; } static int verb_dns(int argc, char **argv, void *userdata) { @@ -1997,19 +1995,11 @@ static int verb_dns(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL); - r = call_dns(bus, argv + 2, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - &error); + r = call_dns(bus, argv + 2, bus_resolve_mgr, &error, true); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = call_dns(bus, argv + 2, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - &error); + r = call_dns(bus, argv + 2, bus_network_mgr, &error, true); } if (r < 0) { if (arg_ifindex_permissive && @@ -2022,18 +2012,12 @@ static int verb_dns(int argc, char **argv, void *userdata) { return 0; } -static int call_domain(sd_bus *bus, char **domain, const char *destination, const char *path, const char *interface, sd_bus_error *error) { +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 = sd_bus_message_new_method_call( - bus, - &req, - destination, - path, - interface, - "SetLinkDomains"); + r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains"); if (r < 0) return bus_log_create_error(r); @@ -2092,19 +2076,11 @@ static int verb_domain(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL); - r = call_domain(bus, argv + 2, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - &error); + r = call_domain(bus, argv + 2, bus_resolve_mgr, &error); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = call_domain(bus, argv + 2, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - &error); + r = call_domain(bus, argv + 2, bus_network_mgr, &error); } if (r < 0) { if (arg_ifindex_permissive && @@ -2140,27 +2116,11 @@ static int verb_default_route(int argc, char **argv, void *userdata) { if (b < 0) return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "SetLinkDefaultRoute", - &error, - NULL, - "ib", arg_ifindex, b); + r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "SetLinkDefaultRoute", - &error, - NULL, - "ib", arg_ifindex, b); + r = bus_call_method(bus, bus_network_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b); } if (r < 0) { if (arg_ifindex_permissive && @@ -2192,27 +2152,11 @@ static int verb_llmnr(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "SetLinkLLMNR", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "SetLinkLLMNR", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_network_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]); } if (r < 0) { if (arg_ifindex_permissive && @@ -2244,23 +2188,13 @@ static int verb_mdns(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "SetLinkMulticastDNS", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", + bus_network_mgr, "SetLinkMulticastDNS", &error, NULL, @@ -2296,23 +2230,13 @@ static int verb_dns_over_tls(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "SetLinkDNSOverTLS", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSOverTLS", &error, NULL, "is", arg_ifindex, argv[2]); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", + bus_network_mgr, "SetLinkDNSOverTLS", &error, NULL, @@ -2348,27 +2272,11 @@ static int verb_dnssec(int argc, char **argv, void *userdata) { if (argc < 3) return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "SetLinkDNSSEC", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "SetLinkDNSSEC", - &error, - NULL, - "is", arg_ifindex, argv[2]); + r = bus_call_method(bus, bus_network_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]); } if (r < 0) { if (arg_ifindex_permissive && @@ -2381,17 +2289,11 @@ static int verb_dnssec(int argc, char **argv, void *userdata) { return 0; } -static int call_nta(sd_bus *bus, char **nta, const char *destination, const char *path, const char *interface, sd_bus_error *error) { +static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; int r; - r = sd_bus_message_new_method_call( - bus, - &req, - destination, - path, - interface, - "SetLinkDNSSECNegativeTrustAnchors"); + r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNSSECNegativeTrustAnchors"); if (r < 0) return bus_log_create_error(r); @@ -2442,19 +2344,11 @@ static int verb_nta(int argc, char **argv, void *userdata) { } } - r = call_nta(bus, clear ? NULL : argv + 2, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - &error); + r = call_nta(bus, clear ? NULL : argv + 2, bus_resolve_mgr, &error); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = call_nta(bus, clear ? NULL : argv + 2, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - &error); + r = call_nta(bus, clear ? NULL : argv + 2, bus_network_mgr, &error); } if (r < 0) { if (arg_ifindex_permissive && @@ -2483,27 +2377,11 @@ static int verb_revert_link(int argc, char **argv, void *userdata) { if (arg_ifindex <= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required."); - r = sd_bus_call_method( - bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "RevertLink", - &error, - NULL, - "i", arg_ifindex); + r = bus_call_method(bus, bus_resolve_mgr, "RevertLink", &error, NULL, "i", arg_ifindex); if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) { sd_bus_error_free(&error); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "RevertLinkDNS", - &error, - NULL, - "i", arg_ifindex); + r = bus_call_method(bus, bus_network_mgr, "RevertLinkDNS", &error, NULL, "i", arg_ifindex); } if (r < 0) { if (arg_ifindex_permissive && @@ -2516,6 +2394,48 @@ static int verb_revert_link(int argc, char **argv, void *userdata) { return 0; } +static int verb_log_level(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + if (argc == 1) { + _cleanup_free_ char *level = NULL; + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.resolve1", + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + "LogLevel", + &error, + &level); + if (r < 0) + return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r)); + + puts(level); + + } else { + assert(argc == 2); + + r = sd_bus_set_property( + bus, + "org.freedesktop.resolve1", + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + "LogLevel", + &error, + "s", + argv[1]); + if (r < 0) + return log_error_errno(r, "Failed to set log level: %s", bus_error_message(&error, r)); + } + + return 0; +} + static void help_protocol_types(void) { if (arg_legend) puts("Known protocol types:"); @@ -3190,6 +3110,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) { { "dnssec", VERB_ANY, 3, 0, verb_dnssec }, { "nta", VERB_ANY, VERB_ANY, 0, verb_nta }, { "revert", VERB_ANY, 2, 0, verb_revert_link }, + { "log-level", VERB_ANY, 2, 0, verb_log_level }, {} }; @@ -3198,7 +3119,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) { static int translate(const char *verb, const char *single_arg, size_t num_args, char **args, sd_bus *bus) { char **fake, **p; - size_t num, i; + size_t num; assert(verb); assert(num_args == 0 || args); @@ -3209,7 +3130,7 @@ static int translate(const char *verb, const char *single_arg, size_t num_args, *p++ = (char *) verb; if (single_arg) *p++ = (char *) single_arg; - for (i = 0; i < num_args; i++) + for (size_t i = 0; i < num_args; i++) *p++ = args[i]; optind = 0; @@ -3312,9 +3233,7 @@ static int run(int argc, char **argv) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); if (streq(program_invocation_short_name, "resolvconf")) r = resolvconf_parse_argv(argc, argv); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index ffe61bdb9..dba1639a1 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -2,8 +2,10 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" +#include "bus-log-control-api.h" +#include "bus-message-util.h" #include "bus-polkit.h" -#include "bus-util.h" #include "dns-domain.h" #include "memory-util.h" #include "missing_capability.h" @@ -16,6 +18,7 @@ #include "socket-netlink.h" #include "stdio-util.h" #include "strv.h" +#include "syslog-util.h" #include "user-util.h" #include "utf8.h" @@ -452,11 +455,10 @@ finish: static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; Manager *m = userdata; + union in_addr_union a; int family, ifindex; uint64_t flags; - const void *d; DnsQuery *q; - size_t sz; int r; assert(message); @@ -464,20 +466,14 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(message, "ii", &ifindex, &family); + r = sd_bus_message_read(message, "i", &ifindex); if (r < 0) return r; - if (!IN_SET(family, AF_INET, AF_INET6)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - - r = sd_bus_message_read_array(message, 'y', &d, &sz); + r = bus_message_read_in_addr_auto(message, error, &family, &a); if (r < 0) return r; - if (sz != FAMILY_ADDRESS_SIZE(family)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); - r = sd_bus_message_read(message, "t", &flags); if (r < 0) return r; @@ -486,7 +482,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = dns_question_new_reverse(&question, family, d); + r = dns_question_new_reverse(&question, family, &a); if (r < 0) return r; @@ -496,7 +492,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s q->request = sd_bus_message_ref(message); q->request_family = family; - memcpy(&q->request_address, d, sz); + q->request_address = a; q->complete = bus_method_resolve_address_complete; r = dns_query_bus_track(q, message); @@ -1114,7 +1110,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { if (has_root_domain && found <= 0) { /* If there's exactly one SRV RR and it uses - * the root domain as host name, then the + * 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 @@ -1216,19 +1212,26 @@ fail: return r; } -int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) { +int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended) { int r; assert(reply); if (!s) { - if (with_ifindex) - return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0); - else - return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0); + if (with_ifindex) { + if (extended) + return sd_bus_message_append(reply, "(iiayqs)", 0, AF_UNSPEC, 0, 0, NULL); + else + return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0); + } else { + if (extended) + return sd_bus_message_append(reply, "(iayqs)", AF_UNSPEC, 0, 0, NULL); + else + return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0); + } } - r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay"); + r = sd_bus_message_open_container(reply, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay")); if (r < 0) return r; @@ -1246,6 +1249,55 @@ int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex if (r < 0) return r; + if (extended) { + r = sd_bus_message_append(reply, "q", s->port); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", s->server_name); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int bus_property_get_dns_servers_internal( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error, + bool extended) { + + Manager *m = userdata; + DnsServer *s; + Iterator i; + Link *l; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)"); + if (r < 0) + return r; + + LIST_FOREACH(servers, s, m->dns_servers) { + r = bus_dns_server_append(reply, s, true, extended); + if (r < 0) + return r; + } + + HASHMAP_FOREACH(l, m->links, i) + LIST_FOREACH(servers, s, l->dns_servers) { + r = bus_dns_server_append(reply, s, true, extended); + if (r < 0) + return r; + } + return sd_bus_message_close_container(reply); } @@ -1257,33 +1309,46 @@ static int bus_property_get_dns_servers( sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false); +} - Manager *m = userdata; - DnsServer *s; - Iterator i; - Link *l; +static int bus_property_get_dns_servers_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true); +} + +static int bus_property_get_fallback_dns_servers_internal( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error, + bool extended) { + + DnsServer *s, **f = userdata; int r; assert(reply); - assert(m); + assert(f); - r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)"); if (r < 0) return r; - LIST_FOREACH(servers, s, m->dns_servers) { - r = bus_dns_server_append(reply, s, true); + LIST_FOREACH(servers, s, *f) { + r = bus_dns_server_append(reply, s, true, extended); if (r < 0) return r; } - HASHMAP_FOREACH(l, m->links, i) - LIST_FOREACH(servers, s, l->dns_servers) { - r = bus_dns_server_append(reply, s, true); - if (r < 0) - return r; - } - return sd_bus_message_close_container(reply); } @@ -1295,24 +1360,38 @@ static int bus_property_get_fallback_dns_servers( sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false); +} - DnsServer *s, **f = userdata; - int r; +static int bus_property_get_fallback_dns_servers_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true); +} + +static int bus_property_get_current_dns_server_internal( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error, + bool extended) { + + DnsServer *s; assert(reply); - assert(f); + assert(userdata); - r = sd_bus_message_open_container(reply, 'a', "(iiay)"); - if (r < 0) - return r; + s = *(DnsServer **) userdata; - LIST_FOREACH(servers, s, *f) { - r = bus_dns_server_append(reply, s, true); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); + return bus_dns_server_append(reply, s, true, extended); } static int bus_property_get_current_dns_server( @@ -1323,15 +1402,18 @@ static int bus_property_get_current_dns_server( sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false); +} - DnsServer *s; - - assert(reply); - assert(userdata); - - s = *(DnsServer **) userdata; - - return bus_dns_server_append(reply, s, true); +static int bus_property_get_current_dns_server_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true); } static int bus_property_get_domains( @@ -1495,9 +1577,6 @@ static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error assert(m); assert(ret); - if (ifindex <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); if (!l) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); @@ -1514,8 +1593,7 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_ assert(message); assert(handler); - assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(message, "i", &ifindex); + r = bus_message_read_ifindex(message, error, &ifindex); if (r < 0) return r; @@ -1530,6 +1608,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); } +static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error); +} + static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { return call_link_method(userdata, message, bus_link_method_set_domains, error); } @@ -1571,8 +1653,7 @@ static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_e assert(message); assert(m); - assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(message, "i", &ifindex); + r = bus_message_read_ifindex(message, error, &ifindex); if (r < 0) return r; @@ -1842,8 +1923,11 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Manager, mdns_support), 0), SD_BUS_PROPERTY("DNSOverTLS", "s", bus_property_get_dns_over_tls_mode, 0, 0), SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("DNSEx", "a(iiayqs)", bus_property_get_dns_servers_ex, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("FallbackDNS", "a(iiay)", bus_property_get_fallback_dns_servers, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FallbackDNSEx", "a(iiayqs)", bus_property_get_fallback_dns_servers_ex, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CurrentDNSServer", "(iiay)", bus_property_get_current_dns_server, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CurrentDNSServerEx", "(iiayqs)", bus_property_get_current_dns_server_ex, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0), SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0), SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0), @@ -1853,29 +1937,134 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0), SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0), - SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetServerFeatures", NULL, NULL, bus_method_reset_server_features, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResolveHostname", + SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags), + SD_BUS_RESULT("a(iiay)", addresses, "s", canonical, "t", flags), + bus_method_resolve_hostname, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResolveAddress", + SD_BUS_ARGS("i", ifindex, "i", family, "ay", address, "t", flags), + SD_BUS_RESULT("a(is)", names, "t", flags), + bus_method_resolve_address, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResolveRecord", + SD_BUS_ARGS("i", ifindex, "s", name, "q", class, "q", type, "t", flags), + SD_BUS_RESULT("a(iqqay)", records, "t", flags), + bus_method_resolve_record, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResolveService", + SD_BUS_ARGS("i", ifindex, + "s", name, + "s", type, + "s", domain, + "i", family, + "t", flags), + SD_BUS_RESULT("a(qqqsa(iiay)s)", srv_data, + "aay", txt_data, + "s", canonical_name, + "s", canonical_type, + "s", canonical_domain, + "t", flags), + bus_method_resolve_service, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("GetLink", + SD_BUS_ARGS("i", ifindex), + SD_BUS_RESULT("o", path), + bus_method_get_link, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDNS", + SD_BUS_ARGS("i", ifindex, "a(iay)", addresses), + SD_BUS_NO_RESULT, + bus_method_set_link_dns_servers, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDNSEx", + SD_BUS_ARGS("i", ifindex, "a(iayqs)", addresses), + SD_BUS_NO_RESULT, + bus_method_set_link_dns_servers_ex, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDomains", + SD_BUS_ARGS("i", ifindex, "a(sb)", domains), + SD_BUS_NO_RESULT, + bus_method_set_link_domains, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDefaultRoute", + SD_BUS_ARGS("i", ifindex, "b", enable), + SD_BUS_NO_RESULT, + bus_method_set_link_default_route, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkLLMNR", + SD_BUS_ARGS("i", ifindex, "s", mode), + SD_BUS_NO_RESULT, + bus_method_set_link_llmnr, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkMulticastDNS", + SD_BUS_ARGS("i", ifindex, "s", mode), + SD_BUS_NO_RESULT, + bus_method_set_link_mdns, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDNSOverTLS", + SD_BUS_ARGS("i", ifindex, "s", mode), + SD_BUS_NO_RESULT, + bus_method_set_link_dns_over_tls, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSEC", + SD_BUS_ARGS("i", ifindex, "s", mode), + SD_BUS_NO_RESULT, + bus_method_set_link_dnssec, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSECNegativeTrustAnchors", + SD_BUS_ARGS("i", ifindex, "as", names), + SD_BUS_NO_RESULT, + bus_method_set_link_dnssec_negative_trust_anchors, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("RevertLink", + SD_BUS_ARGS("i", ifindex), + SD_BUS_NO_RESULT, + bus_method_revert_link, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("RegisterService", + SD_BUS_ARGS("s", name, + "s", name_template, + "s", type, + "q", service_port, + "q", service_priority, + "q", service_weight, + "aa{say}", txt_datas), + SD_BUS_RESULT("o", service_path), + bus_method_register_service, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("UnregisterService", + SD_BUS_ARGS("o", service_path), + SD_BUS_NO_RESULT, + bus_method_unregister_service, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResetStatistics", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + bus_method_reset_statistics, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("FlushCaches", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + bus_method_flush_caches, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("ResetServerFeatures", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + bus_method_reset_server_features, + SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RegisterService", "sssqqqaa{say}", "o", bus_method_register_service, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnregisterService", "o", NULL, bus_method_unregister_service, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; +const BusObjectImplementation manager_object = { + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + .vtables = BUS_VTABLES(resolve_vtable), + .children = BUS_IMPLEMENTATIONS(&link_object, + &dnssd_object), +}; + static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { Manager *m = userdata; int b, r; @@ -1885,7 +2074,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b r = sd_bus_message_read(message, "b", &b); if (r < 0) { - log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); + bus_log_parse_error(r); return 0; } @@ -1910,25 +2099,13 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to connect to system bus: %m"); - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m); + r = bus_add_implementation(m->bus, &manager_object, m); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); + return r; - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m); + r = bus_log_control_api_register(m->bus); if (r < 0) - return log_error_errno(r, "Failed to register link objects: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to register link enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/dnssd", "org.freedesktop.resolve1.DnssdService", dnssd_vtable, dnssd_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register dnssd objects: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/dnssd", dnssd_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to register dnssd enumerator: %m"); + return r; r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL); if (r < 0) diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h index a499f76ad..28caa64a6 100644 --- a/src/resolve/resolved-bus.h +++ b/src/resolve/resolved-bus.h @@ -1,12 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "bus-object.h" #include "resolved-manager.h" +extern const BusObjectImplementation manager_object; + int manager_connect_bus(Manager *m); int _manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; #define manager_send_changed(manager, ...) _manager_send_changed(manager, __VA_ARGS__, NULL) -int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex); +int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended); int bus_property_get_resolve_support(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index ca5b8e791..6b9927124 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -8,7 +8,10 @@ #include "parse-util.h" #include "resolved-conf.h" #include "resolved-dnssd.h" -#include "resolved-util.h" +#include "resolved-manager.h" +#include "resolved-dns-search-domain.h" +#include "dns-domain.h" +#include "socket-netlink.h" #include "specifier.h" #include "string-table.h" #include "string-util.h" @@ -25,24 +28,33 @@ static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MA DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES); static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { + _cleanup_free_ char *server_name = NULL; union in_addr_union address; int family, r, ifindex = 0; + uint16_t port; DnsServer *s; - _cleanup_free_ char *server_name = NULL; assert(m); assert(word); - r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name); + r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name); if (r < 0) return r; + if (IN_SET(port, 53, 853)) + port = 0; + /* Silently filter out 0.0.0.0 and 127.0.0.53 (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; + /* Filter out duplicates */ - s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex); + s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name); if (s) { /* * Drop the marker. This is used to find the servers @@ -54,7 +66,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons return 0; } - return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name); + 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) { @@ -218,10 +230,15 @@ int config_parse_search_domains( int config_parse_dnssd_service_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) { static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, { 'b', specifier_boot_id, NULL }, { 'H', specifier_host_name, NULL }, - { 'm', specifier_machine_id, NULL }, { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, {} }; DnssdService *s = userdata; @@ -373,11 +390,14 @@ int manager_parse_config_file(Manager *m) { assert(m); - r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf", - CONF_PATHS_NULSTR("systemd/resolved.conf.d"), - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - CONFIG_PARSE_WARN, m); + r = config_parse_many_nulstr( + PKGSYSCONFDIR "/resolved.conf", + CONF_PATHS_NULSTR("systemd/resolved.conf.d"), + "Resolve\0", + config_item_perf_lookup, resolved_gperf_lookup, + CONFIG_PARSE_WARN, + m, + NULL); if (r < 0) return r; diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 4713df587..ac3937cfa 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -14,7 +14,6 @@ enum DnsStubListenerMode { _DNS_STUB_LISTENER_MODE_INVALID = -1 }; -#include "resolved-manager.h" #include "resolved-dns-server.h" int manager_parse_config_file(Manager *m); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ec9bfd7ad..5a4f5c58b 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -59,55 +59,6 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { return sum & UINT32_C(0xFFFF); } -int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { - size_t c = 0; - int r; - - /* Converts the specified hostname into DNSSEC canonicalized - * form. */ - - if (buffer_max < 2) - return -ENOBUFS; - - for (;;) { - r = dns_label_unescape(&n, buffer, buffer_max, 0); - if (r < 0) - return r; - if (r == 0) - break; - - if (buffer_max < (size_t) r + 2) - return -ENOBUFS; - - /* The DNSSEC canonical form is not clear on what to - * do with dots appearing in labels, the way DNS-SD - * does it. Refuse it for now. */ - - if (memchr(buffer, '.', r)) - return -EINVAL; - - ascii_strlower_n(buffer, (size_t) r); - buffer[r] = '.'; - - buffer += r + 1; - c += r + 1; - - buffer_max -= r + 1; - } - - if (c <= 0) { - /* Not even a single label: this is the root domain name */ - - assert(buffer_max > 2); - buffer[0] = '.'; - buffer[1] = 0; - - return 1; - } - - return (int) c; -} - #if HAVE_GCRYPT static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) { diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index dfee7232c..1f70861cd 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -58,8 +58,6 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key); uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke); -int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max); - int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret); typedef enum DnssecNsecResult { diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 05aaa0fb7..63ede7247 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -839,7 +839,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns rds = p->size - saved_size; - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: r = dns_packet_append_uint16(p, rr->srv.priority, NULL); @@ -856,14 +856,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns /* RFC 2782 states "Unless and until permitted by future standards * action, name compression is not to be used for this field." */ - r = dns_packet_append_name(p, rr->srv.name, false, false, NULL); + r = dns_packet_append_name(p, rr->srv.name, false, true, NULL); break; case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: case DNS_TYPE_DNAME: - r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL); + r = dns_packet_append_name(p, rr->ptr.name, true, true, NULL); break; case DNS_TYPE_HINFO: @@ -906,11 +906,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns break; case DNS_TYPE_SOA: - r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL); + r = dns_packet_append_name(p, rr->soa.mname, true, true, NULL); if (r < 0) goto fail; - r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL); + r = dns_packet_append_name(p, rr->soa.rname, true, true, NULL); if (r < 0) goto fail; @@ -938,7 +938,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns if (r < 0) goto fail; - r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL); + r = dns_packet_append_name(p, rr->mx.exchange, true, true, NULL); break; case DNS_TYPE_LOC: @@ -1125,7 +1125,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns case DNS_TYPE_OPT: case DNS_TYPE_OPENPGPKEY: - case _DNS_TYPE_INVALID: /* unparseable */ + case _DNS_TYPE_INVALID: /* unparsable */ default: r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL); @@ -1815,8 +1815,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl break; } else { dns_packet_rewind(p, pos); - rr->unparseable = true; - goto unparseable; + rr->unparsable = true; + goto unparsable; } } @@ -2059,7 +2059,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */ case DNS_TYPE_OPENPGPKEY: default: - unparseable: + unparsable: r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); break; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index d6eca6dfd..906158c5c 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -94,7 +94,7 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) { } static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) { - DnsTransaction *t; + _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL; int r; assert(c); @@ -105,39 +105,26 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource r = dns_transaction_new(&t, c->scope, key); if (r < 0) return r; - } else { - if (set_contains(c->transactions, t)) - return 0; - } - - r = set_ensure_allocated(&c->transactions, NULL); - if (r < 0) - goto gc; - - r = set_ensure_allocated(&t->notify_query_candidates, NULL); - if (r < 0) - goto gc; + } else if (set_contains(c->transactions, t)) + return 0; r = set_ensure_allocated(&t->notify_query_candidates_done, NULL); if (r < 0) - goto gc; + return r; - r = set_put(t->notify_query_candidates, c); + r = set_ensure_put(&t->notify_query_candidates, NULL, c); if (r < 0) - goto gc; + return r; - r = set_put(c->transactions, t); + r = set_ensure_put(&c->transactions, NULL, t); if (r < 0) { (void) set_remove(t->notify_query_candidates, c); - goto gc; + return r; } t->clamp_ttl = c->query->clamp_ttl; + TAKE_PTR(t); return 1; - -gc: - dns_transaction_gc(t); - return r; } static int dns_query_candidate_go(DnsQueryCandidate *c) { @@ -513,7 +500,7 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { } static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { - DnsQueryCandidate *c; + _cleanup_(dns_query_candidate_freep) DnsQueryCandidate *c = NULL; int r; assert(q); @@ -524,24 +511,21 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { return r; /* If this a single-label domain on DNS, we might append a suitable search domain first. */ - if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0 && - dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna))) { - /* OK, we need a search domain now. Let's find one for this scope */ + if (!FLAGS_SET(q->flags, SD_RESOLVED_NO_SEARCH) && + dns_scope_name_wants_search_domain(s, dns_question_first_name(q->question_idna))) { + /* OK, we want a search domain now. Let's find one for this scope */ r = dns_query_candidate_next_search_domain(c); - if (r <= 0) /* if there's no search domain, then we won't add any transaction. */ - goto fail; + if (r < 0) + return r; } r = dns_query_candidate_setup_transactions(c); if (r < 0) - goto fail; + return r; + TAKE_PTR(c); return 0; - -fail: - dns_query_candidate_free(c); - return r; } static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 5ee946bc7..fe8a21955 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -10,8 +10,8 @@ typedef struct DnsQuery DnsQuery; #include "resolved-dns-answer.h" #include "resolved-dns-question.h" -#include "resolved-dns-stream.h" #include "resolved-dns-search-domain.h" +#include "resolved-dns-transaction.h" struct DnsQueryCandidate { DnsQuery *query; @@ -102,6 +102,8 @@ enum { }; DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c); +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryCandidate*, dns_query_candidate_free); + void dns_query_candidate_notify(DnsQueryCandidate *c); int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 6ba26a24b..fa43dd089 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -474,11 +474,11 @@ static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) { case DNS_TYPE_OPENPGPKEY: default: - if (!rr->unparseable) + if (!rr->unparsable) free(rr->generic.data); } - if (rr->unparseable) + if (rr->unparsable) free(rr->generic.data); free(rr->wire_format); @@ -563,10 +563,10 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou /* Check if a and b are the same, but don't look at their keys */ - if (a->unparseable != b->unparseable) + if (a->unparsable != b->unparsable) return 0; - switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) { + switch (a->unparsable ? _DNS_TYPE_INVALID : a->key->type) { case DNS_TYPE_SRV: r = dns_name_equal(a->srv.name, b->srv.name); @@ -828,7 +828,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { dns_resource_key_to_string(rr->key, k, sizeof(k)); - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: r = asprintf(&s, "%s %u %u %u %s", @@ -1175,7 +1175,7 @@ ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) { assert(rr); assert(out); - switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + switch(rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: case DNS_TYPE_PTR: case DNS_TYPE_NS: @@ -1343,7 +1343,7 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash * dns_resource_key_hash_func(rr->key, state); - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state); @@ -1510,9 +1510,9 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) { copy->expiry = rr->expiry; copy->n_skip_labels_signer = rr->n_skip_labels_signer; copy->n_skip_labels_source = rr->n_skip_labels_source; - copy->unparseable = rr->unparseable; + copy->unparsable = rr->unparsable; - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: copy->srv.priority = rr->srv.priority; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 291447f00..6c824f796 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -102,7 +102,7 @@ struct DnsResourceRecord { /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */ unsigned n_skip_labels_source; - bool unparseable:1; + bool unparsable:1; bool wire_format_canonical:1; void *wire_format; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index d7e7b5a85..bd4b59ea8 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -447,8 +447,8 @@ static int dns_scope_socket( return TAKE_FD(fd); } -int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) { - return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port, NULL); +int dns_scope_socket_udp(DnsScope *s, DnsServer *server) { + return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, dns_server_port(server), NULL); } int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) { @@ -496,9 +496,8 @@ DnsScopeMatch dns_scope_good_domain( assert(s); assert(domain); - /* Checks if the specified domain is something to look up on - * this scope. Note that this accepts non-qualified hostnames, - * i.e. those without any search path prefixed yet. */ + /* Checks if the specified domain is something to look up on this scope. Note that this accepts + * non-qualified hostnames, i.e. those without any search path suffixed. */ if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) return DNS_SCOPE_NO; @@ -620,7 +619,7 @@ DnsScopeMatch dns_scope_good_domain( manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative * for single-label names, i.e. one label. This is - * particular relevant as it means a "." route on some + * particularly relevant as it means a "." route on some * other scope won't pull all traffic away from * us. (If people actually want to pull traffic away * from us they should turn off LLMNR on the @@ -652,20 +651,21 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) { if (s->protocol == DNS_PROTOCOL_DNS) { - /* On classic DNS, looking up non-address RRs is always - * fine. (Specifically, we want to permit looking up - * DNSKEY and DS records on the root and top-level - * domains.) */ + /* On classic DNS, looking up non-address RRs is always fine. (Specifically, we want to + * permit looking up DNSKEY and DS records on the root and top-level domains.) */ if (!dns_resource_key_is_address(key)) return true; - /* However, we refuse to look up A and AAAA RRs on the - * root and single-label domains, under the assumption - * that those should be resolved via LLMNR or search - * path only, and should not be leaked onto the - * internet. */ - return !(dns_name_is_single_label(dns_resource_key_name(key)) || - dns_name_is_root(dns_resource_key_name(key))); + /* Unless explicitly overridden, we refuse to look up A and AAAA RRs on the root and + * single-label domains, under the assumption that those should be resolved via LLMNR or + * search path only, and should not be leaked onto the internet. */ + const char* name = dns_resource_key_name(key); + + if (!s->manager->resolve_unicast_single_label && + dns_name_is_single_label(name)) + return false; + + return !dns_name_is_root(name); } /* On mDNS and LLMNR, send A and AAAA queries only on the @@ -1170,7 +1170,7 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { return s->manager->search_domains; } -bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { +bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name) { assert(s); if (s->protocol != DNS_PROTOCOL_DNS) @@ -1254,11 +1254,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { if (!scope->announced && dns_resource_key_is_dnssd_ptr(z->rr->key)) { if (!set_contains(types, dns_resource_key_name(z->rr->key))) { - r = set_ensure_allocated(&types, &dns_name_hash_ops); - if (r < 0) - return log_debug_errno(r, "Failed to allocate set: %m"); - - r = set_put(types, dns_resource_key_name(z->rr->key)); + r = set_ensure_put(&types, &dns_name_hash_ops, dns_resource_key_name(z->rr->key)); if (r < 0) return log_debug_errno(r, "Failed to add item to set: %m"); } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index f4b45c4f2..8b1a95855 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -2,18 +2,19 @@ #pragma once #include "list.h" +#include "ratelimit.h" +typedef struct DnsQueryCandidate DnsQueryCandidate; typedef struct DnsScope DnsScope; #include "resolved-dns-cache.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" -#include "resolved-dns-query.h" + #include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" #include "resolved-dns-stream.h" #include "resolved-dns-zone.h" -#include "resolved-link.h" typedef enum DnsScopeMatch { DNS_SCOPE_NO, @@ -74,7 +75,7 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec); int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p); int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address); -int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port); +int dns_scope_socket_udp(DnsScope *s, DnsServer *server); DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain); bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key); @@ -98,7 +99,7 @@ void dns_scope_dump(DnsScope *s, FILE *f); DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s); -bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); +bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name); bool dns_scope_network_good(DnsScope *s); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index 21c2442c5..425a46334 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -3,6 +3,8 @@ #include "alloc-util.h" #include "dns-domain.h" #include "resolved-dns-search-domain.h" +#include "resolved-link.h" +#include "resolved-manager.h" int dns_search_domain_new( Manager *m, diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h index d0c2914d3..df0b2f140 100644 --- a/src/resolve/resolved-dns-search-domain.h +++ b/src/resolve/resolved-dns-search-domain.h @@ -1,18 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "list.h" #include "macro.h" typedef struct DnsSearchDomain DnsSearchDomain; +typedef struct Link Link; +typedef struct Manager Manager; typedef enum DnsSearchDomainType { DNS_SEARCH_DOMAIN_SYSTEM, DNS_SEARCH_DOMAIN_LINK, } DnsSearchDomainType; -#include "resolved-link.h" -#include "resolved-manager.h" - struct DnsSearchDomain { Manager *manager; diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 6016af97d..9c221e198 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -6,6 +6,7 @@ #include "resolved-bus.h" #include "resolved-dns-server.h" #include "resolved-dns-stub.h" +#include "resolved-manager.h" #include "resolved-resolv-conf.h" #include "siphash24.h" #include "string-table.h" @@ -25,6 +26,7 @@ int dns_server_new( Link *l, int family, const union in_addr_union *in_addr, + uint16_t port, int ifindex, const char *server_name) { @@ -46,7 +48,7 @@ int dns_server_new( return -E2BIG; } - if (server_name) { + if (!isempty(server_name)) { name = strdup(server_name); if (!name) return -ENOMEM; @@ -62,6 +64,7 @@ int dns_server_new( .type = type, .family = family, .address = *in_addr, + .port = port, .ifindex = ifindex, .server_name = TAKE_PTR(name), }; @@ -116,6 +119,7 @@ static DnsServer* dns_server_free(DnsServer *s) { #endif free(s->server_string); + free(s->server_string_full); free(s->server_name); return mfree(s); } @@ -222,7 +226,7 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { if (s->verified_feature_level != level) { log_debug("Verified we get a response at feature level %s from DNS server %s.", dns_server_feature_level_to_string(level), - dns_server_string(s)); + strna(dns_server_string_full(s))); s->verified_feature_level = level; } @@ -359,7 +363,7 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level dns_server_reset_counters(s); } - log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s)); + log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s))); } static bool dns_server_grace_period_expired(DnsServer *s) { @@ -413,7 +417,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { log_info("Grace period over, resuming full feature set (%s) for DNS server %s.", dns_server_feature_level_to_string(s->possible_feature_level), - dns_server_string(s)); + strna(dns_server_string_full(s))); dns_server_flush_cache(s); @@ -499,7 +503,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { log_full(log_level, "Using degraded feature set %s instead of %s for DNS server %s.", dns_server_feature_level_to_string(s->possible_feature_level), - dns_server_feature_level_to_string(p), dns_server_string(s)); + dns_server_feature_level_to_string(p), strna(dns_server_string_full(s))); } } @@ -547,13 +551,37 @@ int dns_server_ifindex(const DnsServer *s) { return 0; } +uint16_t dns_server_port(const DnsServer *s) { + assert(s); + + if (s->port > 0) + return s->port; + + return 53; +} + const char *dns_server_string(DnsServer *server) { assert(server); if (!server->server_string) (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string); - return strna(server->server_string); + return server->server_string; +} + +const char *dns_server_string_full(DnsServer *server) { + assert(server); + + if (!server->server_string_full) + (void) in_addr_port_ifindex_name_to_string( + server->family, + &server->address, + server->port, + dns_server_ifindex(server), + server->server_name, + &server->server_string_full); + + return server->server_string_full; } bool dns_server_dnssec_supported(DnsServer *server) { @@ -585,8 +613,8 @@ void dns_server_warn_downgrade(DnsServer *server) { log_struct(LOG_NOTICE, "MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR, - LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)), - "DNS_SERVER=%s", dns_server_string(server), + LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", strna(dns_server_string_full(server))), + "DNS_SERVER=%s", strna(dns_server_string_full(server)), "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level)); server->warned_downgrade = true; @@ -597,7 +625,9 @@ static void dns_server_hash_func(const DnsServer *s, struct siphash *state) { siphash24_compress(&s->family, sizeof(s->family), state); siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state); + siphash24_compress(&s->port, sizeof(s->port), state); siphash24_compress(&s->ifindex, sizeof(s->ifindex), state); + siphash24_compress_string(s->server_name, state); } static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) { @@ -611,11 +641,15 @@ static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) { if (r != 0) return r; + r = CMP(x->port, y->port); + if (r != 0) + return r; + r = CMP(x->ifindex, y->ifindex); if (r != 0) return r; - return 0; + return streq_ptr(x->server_name, y->server_name); } DEFINE_HASH_OPS(dns_server_hash_ops, DnsServer, dns_server_hash_func, dns_server_compare_func); @@ -654,11 +688,15 @@ void dns_server_mark_all(DnsServer *first) { dns_server_mark_all(first->servers_next); } -DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) { +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 && s->ifindex == ifindex) + if (s->family == family && + in_addr_equal(family, &s->address, in_addr) > 0 && + s->port == port && + s->ifindex == ifindex && + streq_ptr(s->server_name, name)) return s; return NULL; @@ -689,7 +727,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { if (s) log_debug("Switching to %s DNS server %s.", dns_server_type_to_string(s->type), - dns_server_string(s)); + strna(dns_server_string_full(s))); dns_server_unref(m->current_dns_server); m->current_dns_server = dns_server_ref(s); @@ -829,7 +867,7 @@ void dns_server_dump(DnsServer *s, FILE *f) { f = stdout; fputs("[Server ", f); - fputs(dns_server_string(s), f); + fputs(strna(dns_server_string_full(s)), f); fputs(" type=", f); fputs(dns_server_type_to_string(s->type), f); diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 889c80a20..464e8dc25 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -2,8 +2,18 @@ #pragma once #include "in-addr-util.h" +#include "list.h" +#include "resolve-util.h" +#include "time-util.h" +typedef struct DnsScope DnsScope; typedef struct DnsServer DnsServer; +typedef struct DnsStream DnsStream; +typedef struct DnsPacket DnsPacket; +typedef struct Link Link; +typedef struct Manager Manager; + +#include "resolved-dnstls.h" typedef enum DnsServerType { DNS_SERVER_SYSTEM, @@ -35,10 +45,6 @@ typedef enum DnsServerFeatureLevel { const char* dns_server_feature_level_to_string(int i) _const_; int dns_server_feature_level_from_string(const char *s) _pure_; -#include "resolved-dnstls.h" -#include "resolved-link.h" -#include "resolved-manager.h" - struct DnsServer { Manager *manager; @@ -50,10 +56,11 @@ struct DnsServer { int family; union in_addr_union address; int ifindex; /* for IPv6 link-local DNS servers */ + uint16_t port; + char *server_name; char *server_string; - - char *server_name; + char *server_string_full; /* The long-lived stream towards this server. */ DnsStream *stream; @@ -96,6 +103,7 @@ int dns_server_new( Link *link, int family, const union in_addr_union *address, + uint16_t port, int ifindex, const char *server_string); @@ -117,13 +125,15 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s); int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level); const char *dns_server_string(DnsServer *server); +const char *dns_server_string_full(DnsServer *server); int dns_server_ifindex(const DnsServer *s); +uint16_t dns_server_port(const DnsServer *s); bool dns_server_dnssec_supported(DnsServer *server); void dns_server_warn_downgrade(DnsServer *server); -DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex); +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name); void dns_server_unlink_all(DnsServer *first); void dns_server_unlink_marked(DnsServer *first); diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index 2a1069499..d4c49e673 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -8,6 +8,7 @@ #include "io-util.h" #include "missing_network.h" #include "resolved-dns-stream.h" +#include "resolved-manager.h" #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC) #define DNS_STREAMS_MAX 128 @@ -86,11 +87,9 @@ static int dns_stream_complete(DnsStream *s, int error) { } static int dns_stream_identify(DnsStream *s) { - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) + + CMSG_SPACE(int) + /* for the TTL */ + + EXTRA_CMSG_SPACE /* kernel appears to require extra space */) control; struct msghdr mh = {}; struct cmsghdr *cmsg; socklen_t sl; @@ -191,7 +190,7 @@ static int dns_stream_identify(DnsStream *s) { s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr); if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) { - uint32_t ifindex = htobe32(s->ifindex); + be32_t ifindex = htobe32(s->ifindex); /* Make sure all packets for this connection are sent on the same interface */ if (s->local.sa.sa_family == AF_INET) { diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index 1013f6e45..9fd8f5a34 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -1,9 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "sd-event.h" + +#include "ordered-set.h" #include "socket-util.h" +typedef struct DnsServer DnsServer; typedef struct DnsStream DnsStream; +typedef struct DnsTransaction DnsTransaction; +typedef struct Manager Manager; + +#include "resolved-dns-packet.h" +#include "resolved-dnstls.h" typedef enum DnsStreamType { DNS_STREAM_LOOKUP, /* Outgoing connection to a classic DNS server */ @@ -14,11 +23,6 @@ typedef enum DnsStreamType { _DNS_STREAM_TYPE_INVALID = -1, } DnsStreamType; -#include "resolved-dns-packet.h" -#include "resolved-dns-transaction.h" -#include "resolved-dnstls.h" -#include "resolved-manager.h" - #define DNS_STREAM_WRITE_TLS_DATA 1 /* Streams are used by three subsystems: diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index ce994a7ee..03edbe26d 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -278,7 +278,7 @@ static int dns_stub_stream_complete(DnsStream *s, int error) { } static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { - DnsQuery *q = NULL; + _cleanup_(dns_query_freep) DnsQuery *q = NULL; int r; assert(m); @@ -289,52 +289,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { in_addr_is_localhost(p->family, &p->destination) <= 0) { log_error("Got packet on unexpected IP range, refusing."); dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); - goto fail; + return; } r = dns_packet_extract(p); if (r < 0) { log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m"); dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false); - goto fail; + return; } if (!DNS_PACKET_VERSION_SUPPORTED(p)) { log_debug("Got EDNS OPT field with unsupported version number."); dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false); - goto fail; + return; } if (dns_type_is_obsolete(p->question->keys[0]->type)) { log_debug("Got message with obsolete key type, refusing."); dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); - goto fail; + return; } if (dns_type_is_zone_transer(p->question->keys[0]->type)) { log_debug("Got request for zone transfer, refusing."); dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); - goto fail; + return; } if (!DNS_PACKET_RD(p)) { /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */ log_debug("Got request with recursion disabled, refusing."); dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false); - goto fail; + return; } if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) { log_debug("Got request with DNSSEC CD bit set, refusing."); dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); - goto fail; + return; } r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH); if (r < 0) { log_error_errno(r, "Failed to generate query object: %m"); dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); - goto fail; + return; } /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */ @@ -348,30 +348,23 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { /* Remember which queries belong to this stream, so that we can cancel them when the stream * is disconnected early */ - r = set_ensure_allocated(&s->queries, &trivial_hash_ops); + r = set_ensure_put(&s->queries, NULL, q); if (r < 0) { log_oom(); - goto fail; - } - - if (set_put(s->queries, q) < 0) { - log_oom(); - goto fail; + return; } + assert(r > 0); } r = dns_query_go(q); if (r < 0) { log_error_errno(r, "Failed to start query: %m"); dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); - goto fail; + return; } log_debug("Processing query..."); - return; - -fail: - dns_query_free(q); + TAKE_PTR(q); } static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 5898308d5..e23ea273e 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -314,7 +314,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { "DNS_TRANSACTION=%" PRIu16, t->id, "DNS_QUESTION=%s", key_str, "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result), - "DNS_SERVER=%s", dns_server_string(t->server), + "DNS_SERVER=%s", strna(dns_server_string_full(t->server)), "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level)); } @@ -398,7 +398,7 @@ static int dns_transaction_pick_server(DnsTransaction *t) { t->n_picked_servers ++; - log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id); + log_debug("Using DNS server %s for transaction %u.", strna(dns_server_string_full(t->server)), t->id); return 1; } @@ -544,8 +544,10 @@ static int on_stream_packet(DnsStream *s) { return 0; } -static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) { - return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53; +static uint16_t dns_transaction_port(DnsTransaction *t) { + if (t->server->port > 0) + return t->server->port; + return DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53; } static int dns_transaction_emit_tcp(DnsTransaction *t) { @@ -576,7 +578,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted)) s = dns_stream_ref(t->server->stream); else - fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa); + fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_transaction_port(t), &sa); type = DNS_STREAM_LOOKUP; break; @@ -1243,7 +1245,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) { dns_transaction_close_connection(t); - fd = dns_scope_socket_udp(t->scope, t->server, 53); + fd = dns_scope_socket_udp(t->scope, t->server); if (fd < 0) return fd; @@ -1501,11 +1503,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { add_known_answers = true; if (t->key->type == DNS_TYPE_ANY) { - r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - r = set_put(keys, t->key); + r = set_ensure_put(&keys, &dns_resource_key_hash_ops, t->key); if (r < 0) return r; } @@ -1571,11 +1569,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { add_known_answers = true; if (other->key->type == DNS_TYPE_ANY) { - r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - r = set_put(keys, other->key); + r = set_ensure_put(&keys, &dns_resource_key_hash_ops, other->key); if (r < 0) return r; } @@ -1800,7 +1794,7 @@ static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) { } static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) { - DnsTransaction *aux; + _cleanup_(dns_transaction_gcp) DnsTransaction *aux = NULL; int r; assert(t); @@ -1833,34 +1827,22 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource } } - r = set_ensure_allocated(&t->dnssec_transactions, NULL); - if (r < 0) - goto gc; - - r = set_ensure_allocated(&aux->notify_transactions, NULL); - if (r < 0) - goto gc; - r = set_ensure_allocated(&aux->notify_transactions_done, NULL); if (r < 0) - goto gc; + return r; - r = set_put(t->dnssec_transactions, aux); + r = set_ensure_put(&t->dnssec_transactions, NULL, aux); if (r < 0) - goto gc; + return r;; - r = set_put(aux->notify_transactions, t); + r = set_ensure_put(&aux->notify_transactions, NULL, t); if (r < 0) { (void) set_remove(t->dnssec_transactions, aux); - goto gc; + return r; } - *ret = aux; + *ret = TAKE_PTR(aux); return 1; - -gc: - dns_transaction_gc(aux); - return r; } static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) { diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index bdfcbc1ac..167541806 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -1,10 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "sd-event.h" + typedef struct DnsTransaction DnsTransaction; typedef enum DnsTransactionState DnsTransactionState; typedef enum DnsTransactionSource DnsTransactionSource; +#include "resolved-dns-answer.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-question.h" +#include "resolved-dns-server.h" + enum DnsTransactionState { DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, @@ -37,13 +45,6 @@ enum DnsTransactionSource { _DNS_TRANSACTION_SOURCE_INVALID = -1 }; -#include "resolved-dns-answer.h" -#include "resolved-dns-packet.h" -#include "resolved-dns-question.h" -#include "resolved-dns-scope.h" -#include "resolved-dns-server.h" -#include "resolved-dns-stream.h" - struct DnsTransaction { DnsScope *scope; @@ -137,6 +138,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key); DnsTransaction* dns_transaction_free(DnsTransaction *t); bool dns_transaction_gc(DnsTransaction *t); +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_gc); + int dns_transaction_go(DnsTransaction *t); void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p); diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 92842bcf8..d68d0c3ba 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -184,11 +184,10 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) { * unsigned. */ NULSTR_FOREACH(name, private_domains) { - if (dns_trust_anchor_knows_domain_positive(d, name)) continue; - r = set_put_strdup(d->negative_by_name, name); + r = set_put_strdup(&d->negative_by_name, name); if (r < 0) return r; } @@ -394,16 +393,10 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u return -EINVAL; } - r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops); + r = set_ensure_consume(&d->negative_by_name, &dns_name_hash_ops, TAKE_PTR(domain)); if (r < 0) return log_oom(); - r = set_put(d->negative_by_name, domain); - if (r < 0) - return log_oom(); - if (r > 0) - domain = NULL; - return 0; } @@ -593,11 +586,7 @@ static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr assert(d); - r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops); - if (r < 0) - return r; - - r = set_put(d->revoked_by_rr, rr); + r = set_ensure_put(&d->revoked_by_rr, &dns_resource_record_hash_ops, rr); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index d5cc2767d..33879d614 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -6,6 +6,7 @@ #include "resolved-dns-packet.h" #include "resolved-dns-zone.h" #include "resolved-dnssd.h" +#include "resolved-manager.h" #include "string-util.h" /* Never allow more than 1K entries */ @@ -161,7 +162,7 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { } static int dns_zone_item_probe_start(DnsZoneItem *i) { - DnsTransaction *t; + _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL; int r; assert(i); @@ -182,25 +183,20 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { return r; } - r = set_ensure_allocated(&t->notify_zone_items, NULL); - if (r < 0) - goto gc; - r = set_ensure_allocated(&t->notify_zone_items_done, NULL); if (r < 0) - goto gc; + return r; - r = set_put(t->notify_zone_items, i); + r = set_ensure_put(&t->notify_zone_items, NULL, i); if (r < 0) - goto gc; + return r; - i->probe_transaction = t; t->probing = true; + i->probe_transaction = TAKE_PTR(t); - if (t->state == DNS_TRANSACTION_NULL) { - + if (i->probe_transaction->state == DNS_TRANSACTION_NULL) { i->block_ready++; - r = dns_transaction_go(t); + r = dns_transaction_go(i->probe_transaction); i->block_ready--; if (r < 0) { @@ -211,10 +207,6 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { dns_zone_item_notify(i); return 0; - -gc: - dns_transaction_gc(t); - return r; } int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { diff --git a/src/resolve/resolved-dnssd-bus.c b/src/resolve/resolved-dnssd-bus.c index f7dcb3bfa..33efb8ef0 100644 --- a/src/resolve/resolved-dnssd-bus.c +++ b/src/resolve/resolved-dnssd-bus.c @@ -6,6 +6,7 @@ #include "resolved-dnssd-bus.h" #include "resolved-dnssd.h" #include "resolved-link.h" +#include "resolved-manager.h" #include "strv.h" #include "user-util.h" @@ -62,16 +63,7 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } -const sd_bus_vtable dnssd_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_METHOD("Unregister", NULL, NULL, bus_dnssd_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("Conflicted", NULL, 0), - - SD_BUS_VTABLE_END -}; - -int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { _cleanup_free_ char *name = NULL; Manager *m = userdata; DnssdService *service; @@ -95,7 +87,7 @@ int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void return 1; } -int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; DnssdService *service; @@ -127,3 +119,19 @@ int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ** return 1; } + +static const sd_bus_vtable dnssd_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_METHOD("Unregister", NULL, NULL, bus_dnssd_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_SIGNAL("Conflicted", NULL, 0), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation dnssd_object = { + "/org/freedesktop/resolve1/dnssd", + "org.freedesktop.resolve1.DnssdService", + .fallback_vtables = BUS_FALLBACK_VTABLES({dnssd_vtable, dnssd_object_find}), + .node_enumerator = dnssd_node_enumerator, +}; diff --git a/src/resolve/resolved-dnssd-bus.h b/src/resolve/resolved-dnssd-bus.h index 9ee2ce17e..403455e89 100644 --- a/src/resolve/resolved-dnssd-bus.h +++ b/src/resolve/resolved-dnssd-bus.h @@ -2,9 +2,8 @@ #include "sd-bus.h" -extern const sd_bus_vtable dnssd_vtable[]; +#include "bus-object.h" -int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +extern const BusObjectImplementation dnssd_object; int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index 78dbed283..4458ad1d2 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -86,10 +86,13 @@ static int dnssd_service_load(Manager *manager, const char *filename) { dropin_dirname = strjoina(service->name, ".dnssd.d"); - r = config_parse_many(filename, DNSSD_SERVICE_DIRS, dropin_dirname, - "Service\0", - config_item_perf_lookup, resolved_dnssd_gperf_lookup, - false, service, NULL); + r = config_parse_many( + filename, DNSSD_SERVICE_DIRS, dropin_dirname, + "Service\0", + config_item_perf_lookup, resolved_dnssd_gperf_lookup, + CONFIG_PARSE_WARN, + service, + NULL); if (r < 0) return r; @@ -153,10 +156,15 @@ static int specifier_dnssd_host_name(char specifier, const void *data, const voi int dnssd_render_instance_name(DnssdService *s, char **ret_name) { static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, { 'b', specifier_boot_id, NULL }, { 'H', specifier_dnssd_host_name, NULL }, - { 'm', specifier_machine_id, NULL }, { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, {} }; _cleanup_free_ char *name = NULL; diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c index 2a4a95989..6b7db7ef8 100644 --- a/src/resolve/resolved-dnstls-gnutls.c +++ b/src/resolve/resolved-dnstls-gnutls.c @@ -8,6 +8,7 @@ #include "resolved-dns-stream.h" #include "resolved-dnstls.h" +#include "resolved-manager.h" #define TLS_PROTOCOL_PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2" DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit); @@ -56,15 +57,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) { } if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) { - stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS; - if (server->family == AF_INET) { - stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr; - stream->dnstls_data.validation.size = 4; - } else { - stream->dnstls_data.validation.data = server->address.in6.s6_addr; - stream->dnstls_data.validation.size = 16; + if (server->server_name) + gnutls_session_set_verify_cert(gs, server->server_name, 0); + else { + stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS; + if (server->family == AF_INET) { + stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr; + stream->dnstls_data.validation.size = 4; + } else { + stream->dnstls_data.validation.data = server->address.in6.s6_addr; + stream->dnstls_data.validation.size = 16; + } + gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0); } - gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0); } if (server->server_name) { diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c index 8f58efacb..f95738649 100644 --- a/src/resolve/resolved-dnstls-openssl.c +++ b/src/resolve/resolved-dnstls-openssl.c @@ -6,10 +6,12 @@ #include #include +#include #include "io-util.h" #include "resolved-dns-stream.h" #include "resolved-dnstls.h" +#include "resolved-manager.h" DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free); DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free); @@ -80,13 +82,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) { if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) { X509_VERIFY_PARAM *v; - const unsigned char *ip; SSL_set_verify(s, SSL_VERIFY_PEER, NULL); v = SSL_get0_param(s); - ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr; - if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0) - return -ECONNREFUSED; + if (server->server_name) { + X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0) + return -ECONNREFUSED; + } else { + const unsigned char *ip; + ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr; + if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0) + return -ECONNREFUSED; + } } if (server->server_name) { diff --git a/src/resolve/resolved-dnstls.h b/src/resolve/resolved-dnstls.h index 1b9121171..6199335b7 100644 --- a/src/resolve/resolved-dnstls.h +++ b/src/resolve/resolved-dnstls.h @@ -3,9 +3,14 @@ #if ENABLE_DNS_OVER_TLS +#include + +typedef struct DnsServer DnsServer; +typedef struct DnsStream DnsStream; typedef struct DnsTlsManagerData DnsTlsManagerData; typedef struct DnsTlsServerData DnsTlsServerData; typedef struct DnsTlsStreamData DnsTlsStreamData; +typedef struct Manager Manager; #if DNS_OVER_TLS_USE_GNUTLS #include "resolved-dnstls-gnutls.h" @@ -15,10 +20,6 @@ typedef struct DnsTlsStreamData DnsTlsStreamData; #error Unknown dependency for supporting DNS-over-TLS #endif -#include "resolved-dns-stream.h" -#include "resolved-dns-transaction.h" -#include "resolved-manager.h" - #define DNSTLS_STREAM_CLOSED 1 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server); diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index c2839d425..6a7f74995 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -100,7 +100,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX); if (r < 0) - return log_error_errno(r, "/etc/hosts:%u: couldn't extract host name: %m", nr); + return log_error_errno(r, "/etc/hosts:%u: couldn't extract hostname: %m", nr); if (r == 0) break; @@ -120,15 +120,10 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { /* Optimize the case where we don't need to store any addresses, by storing * only the name in a dedicated Set instead of the hashmap */ - r = set_ensure_allocated(&hosts->no_address, &dns_name_hash_ops); - if (r < 0) - return log_oom(); - - r = set_put(hosts->no_address, name); + r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops, TAKE_PTR(name)); if (r < 0) return r; - TAKE_PTR(name); continue; } @@ -162,7 +157,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) { } if (!found) - log_warning("/etc/hosts:%u: line is missing any host names", nr); + log_warning("/etc/hosts:%u: line is missing any hostnames", nr); return 0; } diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 049fe9ebd..553da8d25 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -5,6 +5,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include #include "conf-parser.h" #include "resolved-conf.h" +#include "resolved-manager.h" %} struct ConfigPerfItem; %null_strings @@ -17,13 +18,14 @@ struct ConfigPerfItem; %struct-type %includes %% -Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 -Resolve.Domains, config_parse_search_domains, 0, 0 -Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) -Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) -Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) -Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode) -Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache) -Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode) -Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts) \ No newline at end of file +Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 +Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.Domains, config_parse_search_domains, 0, 0 +Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) +Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) +Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) +Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode) +Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache) +Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode) +Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts) +Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label) diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index 2a166c11b..42d4ae7aa 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -6,13 +6,15 @@ #include "alloc-util.h" #include "bus-common-errors.h" +#include "bus-get-properties.h" +#include "bus-message-util.h" #include "bus-polkit.h" -#include "bus-util.h" #include "parse-util.h" #include "resolve-util.h" #include "resolved-bus.h" #include "resolved-link-bus.h" #include "resolved-resolv-conf.h" +#include "socket-netlink.h" #include "stdio-util.h" #include "strv.h" #include "user-util.h" @@ -37,14 +39,15 @@ static int property_get_dns_over_tls_mode( return sd_bus_message_append(reply, "s", dns_over_tls_mode_to_string(link_get_dns_over_tls_mode(l))); } -static int property_get_dns( +static int property_get_dns_internal( sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, - sd_bus_error *error) { + sd_bus_error *error, + bool extended) { Link *l = userdata; DnsServer *s; @@ -53,12 +56,12 @@ static int property_get_dns( assert(reply); assert(l); - r = sd_bus_message_open_container(reply, 'a', "(iay)"); + r = sd_bus_message_open_container(reply, 'a', extended ? "(iayqs)" : "(iay)"); if (r < 0) return r; LIST_FOREACH(servers, s, l->dns_servers) { - r = bus_dns_server_append(reply, s, false); + r = bus_dns_server_append(reply, s, false, extended); if (r < 0) return r; } @@ -66,6 +69,48 @@ static int property_get_dns( return sd_bus_message_close_container(reply); } +static int property_get_dns( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, false); +} + +static int property_get_dns_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, true); +} + +static int property_get_current_dns_server_internal( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error, + bool extended) { + + DnsServer *s; + + assert(reply); + assert(userdata); + + s = *(DnsServer **) userdata; + + return bus_dns_server_append(reply, s, false, extended); +} + static int property_get_current_dns_server( sd_bus *bus, const char *path, @@ -74,15 +119,18 @@ static int property_get_current_dns_server( sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false); +} - DnsServer *s; - - assert(reply); - assert(userdata); - - s = *(DnsServer **) userdata; - - return bus_dns_server_append(reply, s, false); +static int property_get_current_dns_server_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true); } static int property_get_domains( @@ -204,11 +252,10 @@ static int verify_unmanaged_link(Link *l, sd_bus_error *error) { return 0; } -int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ struct in_addr_data *dns = NULL; - size_t allocated = 0, n = 0; +static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) { + struct in_addr_full **dns; Link *l = userdata; - unsigned i; + size_t n; int r; assert(message); @@ -218,52 +265,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ if (r < 0) return r; - r = sd_bus_message_enter_container(message, 'a', "(iay)"); - if (r < 0) - return r; - - for (;;) { - int family; - size_t sz; - const void *d; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_enter_container(message, 'r', "iay"); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_read(message, "i", &family); - if (r < 0) - return r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - - r = sd_bus_message_read_array(message, 'y', &d, &sz); - if (r < 0) - return r; - if (sz != FAMILY_ADDRESS_SIZE(family)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); - - if (!dns_server_address_valid(family, d)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address"); - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(dns, allocated, n+1)) - return -ENOMEM; - - dns[n].family = family; - memcpy(&dns[n].address, d, sz); - n++; - } - - r = sd_bus_message_exit_container(message); + r = bus_message_read_dns_servers(message, error, extended, &dns, &n); if (r < 0) return r; @@ -272,22 +274,26 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ NULL, true, UID_INVALID, &l->manager->polkit_registry, error); if (r < 0) - return r; - if (r == 0) - return 1; /* Polkit will call us back */ + goto finalize; + if (r == 0) { + r = 1; /* Polkit will call us back */ + goto finalize; + } dns_server_mark_all(l->dns_servers); - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { DnsServer *s; - s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0); + s = dns_server_find(l->dns_servers, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name); if (s) dns_server_move_back_and_unmark(s); else { - r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL); - if (r < 0) - goto clear; + r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name); + if (r < 0) { + dns_server_unlink_all(l->dns_servers); + goto finalize; + } } } @@ -299,13 +305,24 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ (void) manager_write_resolv_conf(l->manager); (void) manager_send_changed(l->manager, "DNS"); - return sd_bus_reply_method_return(message, NULL); + r = sd_bus_reply_method_return(message, NULL); + +finalize: + for (size_t i = 0; i < n; i++) + in_addr_full_free(dns[i]); + free(dns); -clear: - dns_server_unlink_all(l->dns_servers); return r; } +int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_link_method_set_dns_servers_internal(message, userdata, error, false); +} + +int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_link_method_set_dns_servers_internal(message, userdata, error, true); +} + int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { Link *l = userdata; int r; @@ -629,7 +646,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i); - r = set_put_strdup(ns, *i); + r = set_put_strdup(&ns, *i); if (r < 0) return r; } @@ -682,35 +699,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error return sd_bus_reply_method_return(message, NULL); } -const sd_bus_vtable link_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0), - SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0), - SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0), - SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0), - SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0), - SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0), - SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0), - SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0), - SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0), - SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), - SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0), - - SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { +static int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { _cleanup_free_ char *e = NULL; Manager *m = userdata; Link *link; @@ -753,7 +742,7 @@ char *link_bus_path(const Link *link) { return p; } -int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +static int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; Link *link; @@ -784,3 +773,81 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char *** return 1; } + +static const sd_bus_vtable link_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0), + SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0), + SD_BUS_PROPERTY("DNSEx", "a(iayqs)", property_get_dns_ex, 0, 0), + SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0), + SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0), + SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0), + SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0), + SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0), + SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0), + SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0), + SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0), + SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0), + + SD_BUS_METHOD_WITH_ARGS("SetDNS", + SD_BUS_ARGS("a(iay)", addresses), + SD_BUS_NO_RESULT, + bus_link_method_set_dns_servers, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDNSEx", + SD_BUS_ARGS("a(iayqs)", addresses), + SD_BUS_NO_RESULT, + bus_link_method_set_dns_servers_ex, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDomains", + SD_BUS_ARGS("a(sb)", domains), + SD_BUS_NO_RESULT, + bus_link_method_set_domains, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDefaultRoute", + SD_BUS_ARGS("b", enable), + SD_BUS_NO_RESULT, + bus_link_method_set_default_route, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetLLMNR", + SD_BUS_ARGS("s", mode), + SD_BUS_NO_RESULT, + bus_link_method_set_llmnr, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetMulticastDNS", + SD_BUS_ARGS("s", mode), + SD_BUS_NO_RESULT, + bus_link_method_set_mdns, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDNSOverTLS", + SD_BUS_ARGS("s", mode), + SD_BUS_NO_RESULT, + bus_link_method_set_dns_over_tls, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDNSSEC", + SD_BUS_ARGS("s", mode), + SD_BUS_NO_RESULT, + bus_link_method_set_dnssec, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("SetDNSSECNegativeTrustAnchors", + SD_BUS_ARGS("as", names), + SD_BUS_NO_RESULT, + bus_link_method_set_dnssec_negative_trust_anchors, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("Revert", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + bus_link_method_revert, + SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +const BusObjectImplementation link_object = { + "/org/freedesktop/resolve1/link", + "org.freedesktop.resolve1.Link", + .fallback_vtables = BUS_FALLBACK_VTABLES({link_vtable, link_object_find}), + .node_enumerator = link_node_enumerator, +}; diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h index 74068a477..fc85ff855 100644 --- a/src/resolve/resolved-link-bus.h +++ b/src/resolve/resolved-link-bus.h @@ -3,15 +3,15 @@ #include "sd-bus.h" +#include "bus-util.h" #include "resolved-link.h" -extern const sd_bus_vtable link_vtable[]; +extern const BusObjectImplementation link_object; -int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); char *link_bus_path(const Link *link); -int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index f19fc2f3a..f52c556bd 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -15,6 +15,7 @@ #include "resolved-link.h" #include "resolved-llmnr.h" #include "resolved-mdns.h" +#include "socket-netlink.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" @@ -251,25 +252,35 @@ int link_process_rtnl(Link *l, sd_netlink_message *m) { return 0; } -static int link_update_dns_server_one(Link *l, const char *name) { +static int link_update_dns_server_one(Link *l, const char *str) { + _cleanup_free_ char *name = NULL; + int family, ifindex, r; union in_addr_union a; DnsServer *s; - int family, r; + uint16_t port; assert(l); - assert(name); + assert(str); - r = in_addr_from_string_auto(name, &family, &a); + r = in_addr_port_ifindex_name_from_string_auto(str, &family, &a, &port, &ifindex, &name); if (r < 0) return r; - s = dns_server_find(l->dns_servers, family, &a, 0); + if (ifindex != 0 && ifindex != l->ifindex) + return -EINVAL; + + /* 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; + + s = dns_server_find(l->dns_servers, family, &a, port, 0, name); if (s) { dns_server_move_back_and_unmark(s); return 0; } - return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL); + return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name); } static int link_update_dns_servers(Link *l) { @@ -493,7 +504,7 @@ static int link_update_dnssec_negative_trust_anchors(Link *l) { if (!ns) return -ENOMEM; - r = set_put_strdupv(ns, ntas); + r = set_put_strdupv(&ns, ntas); if (r < 0) return r; @@ -652,7 +663,9 @@ int link_update(Link *l) { assert(l); link_read_settings(l); - link_load_user(l); + r = link_load_user(l); + if (r < 0) + return r; if (l->llmnr_support != RESOLVE_SUPPORT_NO) { r = manager_llmnr_start(l->manager); @@ -730,7 +743,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) { return s; if (s) - log_debug("Switching to DNS server %s for interface %s.", dns_server_string(s), l->ifname); + log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname); dns_server_unref(l->current_dns_server); l->current_dns_server = dns_server_ref(s); @@ -1207,7 +1220,7 @@ int link_save_user(Link *l) { if (server != l->dns_servers) fputc(' ', f); - v = dns_server_string(server); + v = dns_server_string_full(server); if (!v) { r = -ENOMEM; goto fail; diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index 4b545a553..44d489ce4 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "sd-netlink.h" + #include "in-addr-util.h" #include "ratelimit.h" #include "resolve-util.h" @@ -12,7 +14,6 @@ typedef struct LinkAddress LinkAddress; #include "resolved-dns-scope.h" #include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" -#include "resolved-manager.h" #define LINK_SEARCH_DOMAINS_MAX 256 #define LINK_DNS_SERVERS_MAX 256 diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 0d9d1c48e..1da590b68 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -341,7 +341,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char p = h; r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) - return log_error_errno(r, "Failed to unescape host name: %m"); + return log_error_errno(r, "Failed to unescape hostname: %m"); if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Couldn't find a single label in hostname."); @@ -371,7 +371,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char r = dns_label_escape_new(decoded, r, &n); if (r < 0) - return log_error_errno(r, "Failed to escape host name: %m"); + return log_error_errno(r, "Failed to escape hostname: %m"); if (is_localhost(n)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), @@ -411,7 +411,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, p = fallback_hostname(); r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) - return log_error_errno(r, "Failed to unescape fallback host name: %m"); + return log_error_errno(r, "Failed to unescape fallback hostname: %m"); assert(r > 0); /* The fallback hostname must have at least one label */ @@ -581,8 +581,8 @@ int manager_new(Manager **ret) { .dns_stub_tcp_fd = -1, .hostname_fd = -1, - .llmnr_support = RESOLVE_SUPPORT_YES, - .mdns_support = RESOLVE_SUPPORT_YES, + .llmnr_support = DEFAULT_LLMNR_MODE, + .mdns_support = DEFAULT_MDNS_MODE, .dnssec_mode = DEFAULT_DNSSEC_MODE, .dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE, .enable_cache = DNS_CACHE_MODE_YES, @@ -741,12 +741,9 @@ Manager *manager_free(Manager *m) { int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + CMSG_SPACE(int) /* ttl/hoplimit */ - + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) + + CMSG_SPACE(int) /* ttl/hoplimit */ + + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */) control; union sockaddr_union sa; struct iovec iov; struct msghdr mh = { @@ -930,10 +927,8 @@ static int manager_ipv4_send( uint16_t port, const struct in_addr *source, DnsPacket *p) { - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))]; - } control = {}; + + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {}; union sockaddr_union sa; struct iovec iov; struct msghdr mh = { @@ -988,10 +983,7 @@ static int manager_ipv6_send( const struct in6_addr *source, DnsPacket *p) { - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in6_pktinfo))) control = {}; union sockaddr_union sa; struct iovec iov; struct msghdr mh = { diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 446f258b4..59944df74 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #include "sd-event.h" #include "sd-netlink.h" #include "sd-network.h" @@ -15,10 +17,8 @@ typedef struct Manager Manager; #include "resolved-conf.h" #include "resolved-dns-query.h" #include "resolved-dns-search-domain.h" -#include "resolved-dns-server.h" #include "resolved-dns-stream.h" #include "resolved-dns-trust-anchor.h" -#include "resolved-dnstls.h" #include "resolved-link.h" #define MANAGER_SEARCH_DOMAINS_MAX 256 @@ -70,10 +70,11 @@ struct Manager { LIST_HEAD(DnsSearchDomain, search_domains); unsigned n_search_domains; - bool need_builtin_fallbacks:1; + bool need_builtin_fallbacks; + bool read_resolv_conf; + bool resolve_unicast_single_label; - bool read_resolv_conf:1; - usec_t resolv_conf_mtime; + struct stat resolv_conf_stat; DnsTrustAnchor trust_anchor; @@ -167,6 +168,7 @@ void manager_verify_all(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); +/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we need to figure out why */ #define EXTRA_CMSG_SPACE 1024 int manager_is_own_hostname(Manager *m, const char *name); diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 1896ce337..c6f48d6d8 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -9,59 +9,41 @@ #include "dns-domain.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "ordered-set.h" #include "resolved-conf.h" #include "resolved-dns-server.h" #include "resolved-resolv-conf.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util-label.h" -/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */ -#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf" - -/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */ -#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf" - -/* A static resolv.conf file containing no domains, but only our own DNS server address */ -#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf" - int manager_check_resolv_conf(const Manager *m) { - const char *path; - struct stat st; - int r; + struct stat st, own; assert(m); /* This warns only when our stub listener is disabled and /etc/resolv.conf is a symlink to - * PRIVATE_STATIC_RESOLV_CONF or PRIVATE_STUB_RESOLV_CONF. */ + * PRIVATE_STATIC_RESOLV_CONF. */ if (m->dns_stub_listener_mode != DNS_STUB_LISTENER_NO) return 0; - r = stat("/etc/resolv.conf", &st); - if (r < 0) { + if (stat("/etc/resolv.conf", &st) < 0) { if (errno == ENOENT) return 0; return log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); } - FOREACH_STRING(path, - PRIVATE_STUB_RESOLV_CONF, - PRIVATE_STATIC_RESOLV_CONF) { - - struct stat own; - - /* 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) { - log_warning("DNSStubListener= is disabled, but /etc/resolv.conf is a symlink to %s " - "which expects DNSStubListener= to be enabled.", path); - return -EOPNOTSUPP; - } - } + /* 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) + 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."); return 0; } @@ -112,7 +94,7 @@ int manager_read_resolv_conf(Manager *m) { } /* Have we already seen the file? */ - if (timespec_load(&st.st_mtim) == m->resolv_conf_mtime) + if (stat_inode_unmodified(&st, &m->resolv_conf_stat)) return 0; if (file_is_our_own(&st)) @@ -178,7 +160,7 @@ int manager_read_resolv_conf(Manager *m) { log_syntax(NULL, LOG_DEBUG, "/etc/resolv.conf", n, 0, "Ignoring resolv.conf line: %s", l); } - m->resolv_conf_mtime = timespec_load(&st.st_mtim); + m->resolv_conf_stat = st; /* Flush out all servers and search domains that are still * marked. Those are then ones that didn't appear in the new @@ -355,45 +337,49 @@ int manager_write_resolv_conf(Manager *m) { r = fopen_temporary_label(PRIVATE_UPLINK_RESOLV_CONF, PRIVATE_UPLINK_RESOLV_CONF, &f_uplink, &temp_path_uplink); if (r < 0) - return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m"); + return log_warning_errno(r, "Failed to open new %s for writing: %m", PRIVATE_UPLINK_RESOLV_CONF); (void) fchmod(fileno(f_uplink), 0644); - r = fopen_temporary_label(PRIVATE_STUB_RESOLV_CONF, PRIVATE_STUB_RESOLV_CONF, &f_stub, &temp_path_stub); - if (r < 0) - return log_warning_errno(r, "Failed to open private stub-resolv.conf file for writing: %m"); - - (void) fchmod(fileno(f_stub), 0644); - r = write_uplink_resolv_conf_contents(f_uplink, dns, domains); if (r < 0) { - log_error_errno(r, "Failed to write private resolv.conf contents: %m"); + log_error_errno(r, "Failed to write new %s: %m", PRIVATE_UPLINK_RESOLV_CONF); goto fail; } - if (rename(temp_path_uplink, PRIVATE_UPLINK_RESOLV_CONF) < 0) { - r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m"); - goto fail; + if (m->dns_stub_listener_mode != DNS_STUB_LISTENER_NO) { + r = fopen_temporary_label(PRIVATE_STUB_RESOLV_CONF, PRIVATE_STUB_RESOLV_CONF, &f_stub, &temp_path_stub); + if (r < 0) { + log_warning_errno(r, "Failed to open new %s for writing: %m", PRIVATE_STUB_RESOLV_CONF); + goto fail; + } + + (void) fchmod(fileno(f_stub), 0644); + + r = write_stub_resolv_conf_contents(f_stub, dns, domains); + if (r < 0) { + log_error_errno(r, "Failed to write new %s: %m", PRIVATE_STUB_RESOLV_CONF); + goto fail; + } + + if (rename(temp_path_stub, PRIVATE_STUB_RESOLV_CONF) < 0) + r = log_error_errno(errno, "Failed to move new %s into place: %m", PRIVATE_STUB_RESOLV_CONF); + + } else { + r = symlink_atomic(basename(PRIVATE_UPLINK_RESOLV_CONF), PRIVATE_STUB_RESOLV_CONF); + if (r < 0) + log_error_errno(r, "Failed to symlink %s: %m", PRIVATE_STUB_RESOLV_CONF); } - r = write_stub_resolv_conf_contents(f_stub, dns, domains); + if (rename(temp_path_uplink, PRIVATE_UPLINK_RESOLV_CONF) < 0) + r = log_error_errno(errno, "Failed to move new %s into place: %m", PRIVATE_UPLINK_RESOLV_CONF); + + fail: if (r < 0) { - log_error_errno(r, "Failed to write private stub-resolv.conf contents: %m"); - goto fail; + /* Something went wrong, perform cleanup... */ + (void) unlink(temp_path_uplink); + (void) unlink(temp_path_stub); } - if (rename(temp_path_stub, PRIVATE_STUB_RESOLV_CONF) < 0) { - r = log_error_errno(errno, "Failed to move private stub-resolv.conf file into place: %m"); - goto fail; - } - - return 0; - -fail: - (void) unlink(PRIVATE_UPLINK_RESOLV_CONF); - (void) unlink(temp_path_uplink); - (void) unlink(PRIVATE_STUB_RESOLV_CONF); - (void) unlink(temp_path_stub); - return r; } diff --git a/src/resolve/resolved-util.c b/src/resolve/resolved-util.c deleted file mode 100644 index ac8df6351..000000000 --- a/src/resolve/resolved-util.c +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "alloc-util.h" -#include "macro.h" -#include "resolved-util.h" -#include "socket-netlink.h" - -int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) { - _cleanup_free_ char *buf = NULL, *name = NULL; - const char *m; - int r; - - assert(s); - - m = strchr(s, '#'); - if (m) { - name = strdup(m+1); - if (!name) - return -ENOMEM; - - buf = strndup(s, m - s); - if (!buf) - return -ENOMEM; - - s = buf; - } - - r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex); - if (r < 0) - return r; - - if (server_name) - *server_name = TAKE_PTR(name); - - return r; -} diff --git a/src/resolve/resolved-util.h b/src/resolve/resolved-util.h deleted file mode 100644 index 10ebbc087..000000000 --- a/src/resolve/resolved-util.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "in-addr-util.h" - -int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 27848ccca..16477f28d 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -7,32 +7,40 @@ #include "sd-daemon.h" #include "sd-event.h" +#include "bus-log-control-api.h" #include "capability-util.h" #include "daemon-util.h" #include "main-func.h" #include "mkdir.h" +#include "resolved-bus.h" #include "resolved-conf.h" #include "resolved-manager.h" #include "resolved-resolv-conf.h" #include "selinux-util.h" +#include "service-util.h" #include "signal-util.h" #include "user-util.h" static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; int r; log_setup_service(); - if (argc != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); + r = service_parse_argv("systemd-resolved.service", + "Provide name resolution with caching using DNS, mDNS, LLMNR.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; umask(0022); r = mac_selinux_init(); if (r < 0) - return log_error_errno(r, "SELinux setup failed: %m"); + return r; /* Drop privileges, but only if we have been started as root. If we are not running as root we assume most * privileges are already dropped and we can't create our directory. */ diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 6898c7848..082ad7162 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -15,10 +15,11 @@ #DNS= #FallbackDNS=@DNS_SERVERS@ #Domains= -#LLMNR=yes -#MulticastDNS=yes #DNSSEC=@DEFAULT_DNSSEC_MODE@ #DNSOverTLS=@DEFAULT_DNS_OVER_TLS_MODE@ +#MulticastDNS=@DEFAULT_MDNS_MODE@ +#LLMNR=@DEFAULT_LLMNR_MODE@ #Cache=yes #DNSStubListener=yes #ReadEtcHosts=yes +#ResolveUnicastSingleLabel=no diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 7c6346cb6..bdd96aa9a 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -92,7 +92,6 @@ static void test_packet_from_file(const char* filename, bool canonical) { int main(int argc, char **argv) { int i, N; - _cleanup_free_ char *pkts_glob = NULL; _cleanup_globfree_ glob_t g = {}; char **fnames; @@ -102,7 +101,8 @@ int main(int argc, char **argv) { N = argc - 1; fnames = argv + 1; } else { - pkts_glob = path_join(get_testdata_dir(), "test-resolve/*.pkts"); + _cleanup_free_ char *pkts_glob = NULL; + assert_se(get_testdata_dir("test-resolve/*.pkts", &pkts_glob) >= 0); assert_se(glob(pkts_glob, GLOB_NOSORT, NULL, &g) == 0); N = g.gl_pathc; fnames = g.gl_pathv; diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 840c4fa1d..213177d4d 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -13,26 +13,6 @@ #include "string-util.h" #include "hexdecoct.h" -static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { - char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - - assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); - if (r < 0) - return; - - assert_se(streq(canonicalized, canonical)); -} - -static void test_dnssec_canonicalize(void) { - test_dnssec_canonicalize_one("", ".", 1); - test_dnssec_canonicalize_one(".", ".", 1); - test_dnssec_canonicalize_one("foo", "foo.", 4); - test_dnssec_canonicalize_one("foo.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); - test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); -} - #if HAVE_GCRYPT static void test_dnssec_verify_dns_key(void) { @@ -463,6 +443,136 @@ static void test_dnssec_verify_rrset2(void) { assert_se(result == DNSSEC_VALIDATED); } +static void test_dnssec_verify_rrset3(void) { + + 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, + 0x2b, 0xc5, 0x63, 0x6f, 0x52, 0xa2, 0x60, 0x59, 0x24, 0xb6, 0xc3, 0x43, 0x3d, 0x47, 0x38, 0xd8, + 0x0c, 0xcc, 0x6c, 0x10, 0x49, 0x92, 0x97, 0x6c, 0x7d, 0x32, 0xc2, 0x62, 0x83, 0x34, 0x96, 0xdf, + 0xbd, 0xf9, 0xcc, 0xcf, 0xd9, 0x4d, 0x8b, 0x8a, 0xa9, 0x3c, 0x1f, 0x89, 0xc4, 0xad, 0xd5, 0xbb, + 0x74, 0xf8, 0xee, 0x60, 0x54, 0x7a, 0xec, 0x36, 0x45, 0xf2, 0xec, 0xb9, 0x73, 0x66, 0xae, 0x57, + 0x2d, 0xd4, 0x91, 0x02, 0x99, 0xcd, 0xba, 0xbd, 0x6e, 0xfb, 0xa6, 0xf6, 0x34, 0xce, 0x4c, 0x44, + 0x0b, 0xd2, 0x66, 0xdb, 0x4e, 0x5e, 0x00, 0x72, 0x1b, 0xe5, 0x2f, 0x24, 0xd2, 0xc8, 0x72, 0x37, + 0x97, 0x2b, 0xd0, 0xcd, 0xa9, 0x6b, 0x84, 0x32, 0x56, 0x7a, 0x89, 0x6e, 0x3d, 0x8f, 0x03, 0x9a, + 0x9d, 0x6d, 0xf7, 0xe5, 0x13, 0xd7, 0x4b, 0xbc, 0xe2, 0x6c, 0xd1, 0x18, 0x60, 0x0e, 0x1a, 0xe3, + 0xf9, 0xc0, 0x34, 0x4b, 0x1c, 0x82, 0x17, 0x5e, 0xdf, 0x81, 0x32, 0xd7, 0x5b, 0x30, 0x1d, 0xe0, + 0x29, 0x80, 0x6b, 0xb1, 0x69, 0xbf, 0x3f, 0x12, 0x56, 0xb0, 0x80, 0x91, 0x22, 0x1a, 0x31, 0xd5, + 0x5d, 0x3d, 0xdd, 0x70, 0x5e, 0xcb, 0xc7, 0x2d, 0xb8, 0x3e, 0x54, 0x34, 0xd3, 0x50, 0x89, 0x77, + 0x08, 0xc1, 0xf7, 0x11, 0x6e, 0x57, 0xd7, 0x09, 0x94, 0x20, 0x03, 0x38, 0xc3, 0x3a, 0xd3, 0x93, + 0x8f, 0xd0, 0x65, 0xc5, 0xa1, 0xe0, 0x69, 0x2c, 0xf6, 0x0a, 0xce, 0x01, 0xb6, 0x0d, 0x95, 0xa0, + 0x5d, 0x97, 0x94, 0xc3, 0xf1, 0xcd, 0x49, 0xea, 0x20, 0xd3, 0xa9, 0xa6, 0x67, 0x94, 0x64, 0x17 + }; + + static const uint8_t dnskey_blob[] = { + 0x03, 0x01, 0x00, 0x01, 0xbf, 0xdd, 0x24, 0x95, 0x21, 0x70, 0xa8, 0x5b, 0x19, 0xa6, 0x76, 0xd3, + 0x5b, 0x37, 0xcf, 0x59, 0x0d, 0x3c, 0xdb, 0x0c, 0xcf, 0xd6, 0x19, 0x02, 0xc7, 0x8e, 0x56, 0x4d, + 0x14, 0xb7, 0x9d, 0x71, 0xf4, 0xdd, 0x24, 0x36, 0xc8, 0x32, 0x1c, 0x63, 0xf7, 0xc0, 0xfc, 0xe3, + 0x83, 0xa6, 0x22, 0x8b, 0x6a, 0x34, 0x41, 0x72, 0xaa, 0x95, 0x98, 0x06, 0xac, 0x03, 0xec, 0xc3, + 0xa1, 0x6d, 0x8b, 0x1b, 0xfd, 0xa4, 0x05, 0x72, 0xe6, 0xe0, 0xb9, 0x98, 0x07, 0x54, 0x7a, 0xb2, + 0x55, 0x30, 0x96, 0xa3, 0x22, 0x3b, 0xe0, 0x9d, 0x61, 0xf6, 0xdc, 0x31, 0x2b, 0xc9, 0x2c, 0x12, + 0x06, 0x7f, 0x3c, 0x5d, 0x29, 0x76, 0x01, 0x62, 0xe3, 0x41, 0x41, 0x4f, 0xa6, 0x07, 0xfa, 0x2d, + 0x0c, 0x64, 0x88, 0xd1, 0x56, 0x18, 0x4b, 0x2b, 0xc2, 0x19, 0x7e, 0xd0, 0x1a, 0x8c, 0x2d, 0x8d, + 0x06, 0xdf, 0x4d, 0xaf, 0xd9, 0xe3, 0x31, 0x59, 0xbc, 0xc3, 0x36, 0x22, 0xe7, 0x15, 0xf9, 0xb2, + 0x44, 0x8a, 0x33, 0xd7, 0x6c, 0xf1, 0xcc, 0x37, 0x05, 0x69, 0x32, 0x71, 0x76, 0xd8, 0x50, 0x06, + 0xae, 0x27, 0xed, 0x3b, 0xdb, 0x1a, 0x97, 0x9b, 0xa3, 0x3e, 0x40, 0x42, 0x29, 0xaf, 0x75, 0x1c, + 0xff, 0x1d, 0xaf, 0x85, 0x02, 0xb3, 0x2e, 0x99, 0x67, 0x08, 0x13, 0xd5, 0xda, 0x6d, 0x65, 0xb2, + 0x36, 0x6f, 0x2f, 0x64, 0xe0, 0xfa, 0xd3, 0x81, 0x86, 0x6b, 0x41, 0x3e, 0x91, 0xaa, 0x0a, 0xd3, + 0xb2, 0x92, 0xd9, 0x42, 0x36, 0x8a, 0x11, 0x0b, 0x5b, 0xb0, 0xea, 0xad, 0x76, 0xd5, 0xb4, 0x81, + 0x30, 0xca, 0x5c, 0x4f, 0xd9, 0xea, 0xe7, 0x4b, 0x10, 0x0a, 0x09, 0x4b, 0x73, 0x66, 0xed, 0x8e, + 0x84, 0xa2, 0x4f, 0x93, 0x7e, 0x29, 0xdc, 0x6a, 0xbd, 0x12, 0xa1, 0x3d, 0xd2, 0xd6, 0x2a, 0x67, + 0x99, 0x4d, 0xf3, 0x43 + }; + + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *mx1 = NULL, *mx2 = NULL, *mx3 = NULL, *mx4 = NULL, *rrsig = NULL, *dnskey = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; + + mx1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se"); + assert_se(mx1); + + mx1->mx.priority = 1; + mx1->mx.exchange = strdup("ASPMX.L.GOOGLE.COM"); + assert_se(mx1->mx.exchange); + + log_info("MX: %s", strna(dns_resource_record_to_string(mx1))); + + mx2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se"); + assert_se(mx2); + + mx2->mx.priority = 5; + mx2->mx.exchange = strdup("ALT2.ASPMX.L.GOOGLE.COM"); + assert_se(mx2->mx.exchange); + + log_info("MX: %s", strna(dns_resource_record_to_string(mx2))); + + mx3 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se"); + assert_se(mx3); + + mx3->mx.priority = 10; + mx3->mx.exchange = strdup("ASPMX2.GOOGLEMAIL.COM"); + assert_se(mx3->mx.exchange); + + log_info("MX: %s", strna(dns_resource_record_to_string(mx3))); + + mx4 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se"); + assert_se(mx4); + + mx4->mx.priority = 10; + mx4->mx.exchange = strdup("ASPMX3.GOOGLEMAIL.COM"); + assert_se(mx4->mx.exchange); + + log_info("MX: %s", strna(dns_resource_record_to_string(mx4))); + + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "kodapan.se"); + assert_se(rrsig); + + rrsig->rrsig.type_covered = DNS_TYPE_MX; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 900; + rrsig->rrsig.expiration = 0x5e608a84; + rrsig->rrsig.inception = 0x5e4e1584; + rrsig->rrsig.key_tag = 44028; + rrsig->rrsig.signer = strdup("kodapan.se."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); + + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "kodapan.se"); + assert_se(dnskey); + + dnskey->dnskey.flags = 256; + dnskey->dnskey.protocol = 3; + dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; + dnskey->dnskey.key_size = sizeof(dnskey_blob); + dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob)); + assert_se(dnskey->dnskey.key); + + log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); + + assert_se(dnssec_key_match_rrsig(mx1->key, rrsig) > 0); + assert_se(dnssec_key_match_rrsig(mx2->key, rrsig) > 0); + assert_se(dnssec_key_match_rrsig(mx3->key, rrsig) > 0); + assert_se(dnssec_key_match_rrsig(mx4->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); + + answer = dns_answer_new(4); + assert_se(answer); + assert_se(dns_answer_add(answer, mx1, 0, DNS_ANSWER_AUTHENTICATED) >= 0); + assert_se(dns_answer_add(answer, mx2, 0, DNS_ANSWER_AUTHENTICATED) >= 0); + assert_se(dns_answer_add(answer, mx3, 0, DNS_ANSWER_AUTHENTICATED) >= 0); + assert_se(dns_answer_add(answer, mx4, 0, DNS_ANSWER_AUTHENTICATED) >= 0); + + /* Validate the RR as it if was 2020-02-24 today */ + assert_se(dnssec_verify_rrset(answer, mx1->key, rrsig, dnskey, 1582534685*USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); +} + static void test_dnssec_nsec3_hash(void) { 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 }; @@ -499,14 +609,13 @@ static void test_dnssec_nsec3_hash(void) { int main(int argc, char *argv[]) { - test_dnssec_canonicalize(); - #if HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rfc8080_ed25519_example1(); test_dnssec_verify_rfc8080_ed25519_example2(); test_dnssec_verify_rrset(); test_dnssec_verify_rrset2(); + test_dnssec_verify_rrset3(); test_dnssec_nsec3_hash(); #endif diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c index ca3590066..721bf8732 100644 --- a/src/resolve/test-resolved-etc-hosts.c +++ b/src/resolve/test-resolved-etc-hosts.c @@ -65,7 +65,7 @@ static void test_parse_etc_hosts(void) { "1::2::3 multi.colon\n" "::0 some.where some.other\n" - "0.0.0.0 black.listed\n" + "0.0.0.0 deny.listed\n" "::5\t\t\t \tsome.where\tsome.other foobar.foo.foo\t\t\t\n" " \n", f); assert_se(fflush_and_check(f) >= 0); @@ -123,7 +123,7 @@ static void test_parse_etc_hosts(void) { assert_se( set_contains(hosts.no_address, "some.where")); assert_se( set_contains(hosts.no_address, "some.other")); - assert_se( set_contains(hosts.no_address, "black.listed")); + assert_se( set_contains(hosts.no_address, "deny.listed")); assert_se(!set_contains(hosts.no_address, "foobar.foo.foo")); } diff --git a/src/resolve/test-resolved-util.c b/src/resolve/test-resolved-util.c deleted file mode 100644 index 35bd73c4f..000000000 --- a/src/resolve/test-resolved-util.c +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "log.h" -#include "resolved-util.h" -#include "string-util.h" -#include "tests.h" - - -static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) { - int family, ifindex; - union in_addr_union ua; - _cleanup_free_ char *server_name = NULL; - - assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0); - assert_se(streq_ptr(server_name, expected)); -} - -static void test_in_addr_ifindex_name_from_string_auto(void) { - log_info("/* %s */", __func__); - - test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL); - test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com"); - test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL); - test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com"); -} - -int main(int argc, char **argv) { - test_setup_logging(LOG_DEBUG); - - test_in_addr_ifindex_name_from_string_auto(); - return 0; -} diff --git a/src/run/run.c b/src/run/run.c index 3b2110129..d4ce3966e 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -11,8 +11,9 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "bus-wait-for-jobs.h" #include "calendarspec.h" #include "env-util.h" @@ -41,6 +42,7 @@ static bool arg_wait = false; static const char *arg_unit = NULL; static const char *arg_description = NULL; static const char *arg_slice = NULL; +static bool arg_slice_inherit = false; static bool arg_send_sighup = false; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static const char *arg_host = NULL; @@ -97,6 +99,7 @@ static int help(void) { " -p --property=NAME=VALUE Set service or scope unit property\n" " --description=TEXT Description for unit\n" " --slice=SLICE Run in the specified slice\n" + " --slice-inherit Inherit the slice\n" " --no-block Do not wait until operation finished\n" " -r --remain-after-exit Leave service around until explicitly stopped\n" " --wait Wait until service stopped again\n" @@ -162,6 +165,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SCOPE, ARG_DESCRIPTION, ARG_SLICE, + ARG_SLICE_INHERIT, ARG_SEND_SIGHUP, ARG_SERVICE_TYPE, ARG_EXEC_USER, @@ -194,6 +198,7 @@ static int parse_argv(int argc, char *argv[]) { { "unit", required_argument, NULL, 'u' }, { "description", required_argument, NULL, ARG_DESCRIPTION }, { "slice", required_argument, NULL, ARG_SLICE }, + { "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT }, { "remain-after-exit", no_argument, NULL, 'r' }, { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, { "host", required_argument, NULL, 'H' }, @@ -273,6 +278,10 @@ static int parse_argv(int argc, char *argv[]) { arg_slice = optarg; break; + case ARG_SLICE_INHERIT: + arg_slice_inherit = true; + break; + case ARG_SEND_SIGHUP: arg_send_sighup = true; break; @@ -637,23 +646,50 @@ static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **p } static int transient_cgroup_set_properties(sd_bus_message *m) { + _cleanup_free_ char *name = NULL; + _cleanup_free_ char *slice = NULL; int r; assert(m); - if (!isempty(arg_slice)) { - _cleanup_free_ char *slice = NULL; + if (arg_slice_inherit) { + char *end; - r = unit_name_mangle_with_suffix(arg_slice, "as slice", - arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, - ".slice", &slice); + if (arg_user) + r = cg_pid_get_user_slice(0, &name); + else + r = cg_pid_get_slice(0, &name); if (r < 0) - return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice); + return log_error_errno(r, "Failed to get PID slice: %m"); - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return bus_log_create_error(r); + end = endswith(name, ".slice"); + if (!end) + return -ENXIO; + *end = 0; } + if (!isempty(arg_slice)) { + if (name) { + char *j = strjoin(name, "-", arg_slice); + free_and_replace(name, j); + } else + name = strdup(arg_slice); + if (!name) + return log_oom(); + } + + if (!name) + return 0; + + r = unit_name_mangle_with_suffix(name, "as slice", + arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, + ".slice", &slice); + if (r < 0) + return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice); + + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return bus_log_create_error(r); + return 0; } @@ -943,8 +979,11 @@ typedef struct RunContext { PTYForward *forward; sd_bus_slot *match; - /* The exit data of the unit */ + /* Current state of the unit */ char *active_state; + bool has_job; + + /* The exit data of the unit */ uint64_t inactive_exit_usec; uint64_t inactive_enter_usec; char *result; @@ -975,7 +1014,7 @@ static void run_context_check_done(RunContext *c) { assert(c); if (c->match) - done = STRPTR_IN_SET(c->active_state, "inactive", "failed"); + done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job; else done = true; @@ -986,20 +1025,35 @@ static void run_context_check_done(RunContext *c) { sd_event_exit(c->event, EXIT_SUCCESS); } +static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + bool *b = userdata; + const char *job; + uint32_t id; + int r; + + r = sd_bus_message_read(m, "(uo)", &id, &job); + if (r < 0) + return r; + + *b = id != 0 || !streq(job, "/"); + return 0; +} + static int run_context_update(RunContext *c, const char *path) { static const struct bus_properties_map map[] = { - { "ActiveState", "s", NULL, offsetof(RunContext, active_state) }, - { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) }, - { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) }, - { "Result", "s", NULL, offsetof(RunContext, result) }, - { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) }, - { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) }, - { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) }, - { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) }, - { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) }, - { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) }, - { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) }, + { "ActiveState", "s", NULL, offsetof(RunContext, active_state) }, + { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) }, + { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) }, + { "Result", "s", NULL, offsetof(RunContext, result) }, + { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) }, + { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) }, + { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) }, + { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) }, + { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) }, + { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) }, + { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) }, + { "Job", "(uo)", map_job, offsetof(RunContext, has_job) }, {} }; @@ -1131,13 +1185,7 @@ static int start_transient_service( return r; } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -1385,13 +1433,7 @@ static int start_transient_scope(sd_bus *bus) { return r; } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -1575,13 +1617,7 @@ static int start_transient_trigger( return log_error_errno(r, "Failed to change unit suffix: %m"); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); @@ -1717,7 +1753,7 @@ static int run(int argc, char* argv[]) { else r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); if (arg_scope) r = start_transient_scope(bus); diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 1ccb4f829..dd2b1efb1 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -378,10 +378,13 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) { int add_acls_for_user(int fd, uid_t uid) { _cleanup_(acl_freep) acl_t acl = NULL; - acl_entry_t entry; acl_permset_t permset; + acl_entry_t entry; int r; + assert(fd >= 0); + assert(uid_is_valid(uid)); + acl = acl_get_fd(fd); if (!acl) return -errno; @@ -394,8 +397,8 @@ int add_acls_for_user(int fd, uid_t uid) { return -errno; } - /* We do not recalculate the mask unconditionally here, - * so that the fchmod() mask above stays intact. */ + /* We do not recalculate the mask unconditionally here, so that the fchmod() mask above stays + * intact. */ if (acl_get_permset(entry, &permset) < 0 || acl_add_perm(permset, ACL_READ) < 0) return -errno; @@ -404,5 +407,8 @@ int add_acls_for_user(int fd, uid_t uid) { if (r < 0) return r; - return acl_set_fd(fd, acl); + if (acl_set_fd(fd, acl) < 0) + return -errno; + + return 0; } diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index d565ebd43..38c464c91 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -23,7 +23,7 @@ struct acpi_table_header { uint32_t oem_revision; char asl_compiler_id[4]; uint32_t asl_compiler_revision; -}; +} _packed_; enum { ACPI_FPDT_TYPE_BOOT = 0, @@ -36,12 +36,12 @@ struct acpi_fpdt_header { uint8_t revision; uint8_t reserved[4]; uint64_t ptr; -}; +} _packed_; struct acpi_fpdt_boot_header { char signature[4]; uint32_t length; -}; +} _packed_; enum { ACPI_FPDT_S3PERF_RESUME_REC = 0, @@ -59,7 +59,7 @@ struct acpi_fpdt_boot { uint64_t startup_start; uint64_t exit_services_entry; uint64_t exit_services_exit; -}; +} _packed; int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { _cleanup_free_ char *buf = NULL; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 1175f2336..3d0b93910 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -27,6 +27,7 @@ #include "format-util.h" #include "fs-util.h" #include "io-util.h" +#include "locale-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" @@ -134,12 +135,12 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa if (keyctl(KEYCTL_SET_TIMEOUT, (unsigned long) serial, (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0) - log_debug_errno(errno, "Failed to adjust timeout: %m"); + log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m"); /* Tell everyone to check the keyring */ (void) touch("/run/systemd/ask-password"); - log_debug("Added key to keyring as %" PRIi32 ".", serial); + log_debug("Added key to kernel keyring as %" PRIi32 ".", serial); return 1; } @@ -151,7 +152,7 @@ static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, c r = add_to_keyring(keyname, flags, passwords); if (r < 0) - return log_debug_errno(r, "Failed to add password to keyring: %m"); + return log_debug_errno(r, "Failed to add password to kernel keyring: %m"); return 0; } @@ -304,6 +305,12 @@ int ask_password_plymouth( goto finish; } + if (pollfd[POLL_SOCKET].revents & POLLNVAL || + (notify >= 0 && pollfd[POLL_INOTIFY].revents & POLLNVAL)) { + r = -EBADF; + goto finish; + } + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) (void) flush_fd(notify); @@ -430,6 +437,9 @@ int ask_password_tty( if (!message) message = "Password:"; + if (emoji_enabled()) + message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message); + if (flag_file || ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname)) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); if (notify < 0) @@ -537,6 +547,12 @@ int ask_password_tty( goto finish; } + if ((pollfd[POLL_TTY].revents & POLLNVAL) || + (notify >= 0 && (pollfd[POLL_INOTIFY].revents & POLLNVAL))) { + r = -EBADF; + goto finish; + } + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyname) { (void) flush_fd(notify); @@ -791,14 +807,12 @@ int ask_password_agent( (void) fchmod(fd, 0644); - f = fdopen(fd, "w"); + f = take_fdopen(&fd, "w"); if (!f) { r = -errno; goto finish; } - fd = -1; - signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) { r = -errno; @@ -857,14 +871,10 @@ int ask_password_agent( pollfd[FD_INOTIFY].events = POLLIN; for (;;) { + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; char passphrase[LINE_MAX+1]; - struct msghdr msghdr; struct iovec iovec; struct ucred *ucred; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; ssize_t n; int k; usec_t t; @@ -890,6 +900,13 @@ int ask_password_agent( goto finish; } + if (pollfd[FD_SOCKET].revents & POLLNVAL || + pollfd[FD_SIGNAL].revents & POLLNVAL || + (notify >= 0 && pollfd[FD_INOTIFY].revents & POLLNVAL)) { + r = -EBADF; + goto finish; + } + if (pollfd[FD_SIGNAL].revents & POLLIN) { r = -EINTR; goto finish; @@ -916,12 +933,12 @@ int ask_password_agent( iovec = IOVEC_MAKE(passphrase, sizeof(passphrase)); - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; n = recvmsg_safe(socket_fd, &msghdr, 0); if (IN_SET(n, -EAGAIN, -EINTR)) @@ -938,15 +955,12 @@ int ask_password_agent( continue; } - if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || - control.cmsghdr.cmsg_level != SOL_SOCKET || - control.cmsghdr.cmsg_type != SCM_CREDENTIALS || - control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); + if (!ucred) { log_debug("Received message without credentials. Ignoring."); continue; } - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); if (ucred->uid != 0) { log_debug("Got request from unprivileged user. Ignoring."); continue; diff --git a/src/shared/barrier.c b/src/shared/barrier.c index bb5869dad..80b597b5c 100644 --- a/src/shared/barrier.c +++ b/src/shared/barrier.c @@ -218,10 +218,14 @@ static bool barrier_read(Barrier *b, int64_t comp) { uint64_t buf; int r; - r = poll(pfd, 2, -1); - if (r < 0 && IN_SET(errno, EAGAIN, EINTR)) - continue; - else if (r < 0) + r = poll(pfd, ELEMENTSOF(pfd), -1); + if (r < 0) { + if (IN_SET(errno, EAGAIN, EINTR)) + continue; + goto error; + } + if (pfd[0].revents & POLLNVAL || + pfd[1].revents & POLLNVAL) goto error; if (pfd[1].revents) { diff --git a/src/shared/bond-util.c b/src/shared/bond-util.c new file mode 100644 index 000000000..2296ecd9d --- /dev/null +++ b/src/shared/bond-util.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bond-util.h" +#include "string-table.h" + +static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = { + [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr", + [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup", + [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor", + [NETDEV_BOND_MODE_BROADCAST] = "broadcast", + [NETDEV_BOND_MODE_802_3AD] = "802.3ad", + [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb", + [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode); + +static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = { + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2", + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4", + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3", + [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3", + [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy); + +static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = { + [NETDEV_BOND_LACP_RATE_SLOW] = "slow", + [NETDEV_BOND_LACP_RATE_FAST] = "fast", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate); + +static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = { + [NETDEV_BOND_AD_SELECT_STABLE] = "stable", + [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth", + [NETDEV_BOND_AD_SELECT_COUNT] = "count", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect); + +static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = { + [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none", + [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active", + [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac); + +static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = { + [NETDEV_BOND_ARP_VALIDATE_NONE] = "none", + [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active", + [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup", + [NETDEV_BOND_ARP_VALIDATE_ALL]= "all", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate); + +static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = { + [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any", + [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets); + +static const char *const bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { + [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always", + [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better", + [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect); diff --git a/src/shared/bond-util.h b/src/shared/bond-util.h new file mode 100644 index 000000000..66f86e77f --- /dev/null +++ b/src/shared/bond-util.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#include "macro.h" + +/* + * Maximum number of targets supported by the kernel for a single + * bond netdev. + */ +#define NETDEV_BOND_ARP_TARGETS_MAX 16 + +typedef enum BondMode { + NETDEV_BOND_MODE_BALANCE_RR = BOND_MODE_ROUNDROBIN, + NETDEV_BOND_MODE_ACTIVE_BACKUP = BOND_MODE_ACTIVEBACKUP, + NETDEV_BOND_MODE_BALANCE_XOR = BOND_MODE_XOR, + NETDEV_BOND_MODE_BROADCAST = BOND_MODE_BROADCAST, + NETDEV_BOND_MODE_802_3AD = BOND_MODE_8023AD, + NETDEV_BOND_MODE_BALANCE_TLB = BOND_MODE_TLB, + NETDEV_BOND_MODE_BALANCE_ALB = BOND_MODE_ALB, + _NETDEV_BOND_MODE_MAX, + _NETDEV_BOND_MODE_INVALID = -1 +} BondMode; + +typedef enum BondXmitHashPolicy { + NETDEV_BOND_XMIT_HASH_POLICY_LAYER2 = BOND_XMIT_POLICY_LAYER2, + NETDEV_BOND_XMIT_HASH_POLICY_LAYER34 = BOND_XMIT_POLICY_LAYER34, + NETDEV_BOND_XMIT_HASH_POLICY_LAYER23 = BOND_XMIT_POLICY_LAYER23, + NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23 = BOND_XMIT_POLICY_ENCAP23, + NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34 = BOND_XMIT_POLICY_ENCAP34, + _NETDEV_BOND_XMIT_HASH_POLICY_MAX, + _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1 +} BondXmitHashPolicy; + +typedef enum BondLacpRate { + NETDEV_BOND_LACP_RATE_SLOW, + NETDEV_BOND_LACP_RATE_FAST, + _NETDEV_BOND_LACP_RATE_MAX, + _NETDEV_BOND_LACP_RATE_INVALID = -1, +} BondLacpRate; + +typedef enum BondAdSelect { + NETDEV_BOND_AD_SELECT_STABLE, + NETDEV_BOND_AD_SELECT_BANDWIDTH, + NETDEV_BOND_AD_SELECT_COUNT, + _NETDEV_BOND_AD_SELECT_MAX, + _NETDEV_BOND_AD_SELECT_INVALID = -1, +} BondAdSelect; + +typedef enum BondFailOverMac { + NETDEV_BOND_FAIL_OVER_MAC_NONE, + NETDEV_BOND_FAIL_OVER_MAC_ACTIVE, + NETDEV_BOND_FAIL_OVER_MAC_FOLLOW, + _NETDEV_BOND_FAIL_OVER_MAC_MAX, + _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1, +} BondFailOverMac; + +typedef enum BondArpValidate { + NETDEV_BOND_ARP_VALIDATE_NONE, + NETDEV_BOND_ARP_VALIDATE_ACTIVE, + NETDEV_BOND_ARP_VALIDATE_BACKUP, + NETDEV_BOND_ARP_VALIDATE_ALL, + _NETDEV_BOND_ARP_VALIDATE_MAX, + _NETDEV_BOND_ARP_VALIDATE_INVALID = -1, +} BondArpValidate; + +typedef enum BondArpAllTargets { + NETDEV_BOND_ARP_ALL_TARGETS_ANY, + NETDEV_BOND_ARP_ALL_TARGETS_ALL, + _NETDEV_BOND_ARP_ALL_TARGETS_MAX, + _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1, +} BondArpAllTargets; + +typedef enum BondPrimaryReselect { + NETDEV_BOND_PRIMARY_RESELECT_ALWAYS, + NETDEV_BOND_PRIMARY_RESELECT_BETTER, + NETDEV_BOND_PRIMARY_RESELECT_FAILURE, + _NETDEV_BOND_PRIMARY_RESELECT_MAX, + _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1, +} BondPrimaryReselect; + +const char *bond_mode_to_string(BondMode d) _const_; +BondMode bond_mode_from_string(const char *d) _pure_; + +const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_; +BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_; + +const char *bond_lacp_rate_to_string(BondLacpRate d) _const_; +BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_; + +const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_; +BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_; + +const char *bond_ad_select_to_string(BondAdSelect d) _const_; +BondAdSelect bond_ad_select_from_string(const char *d) _pure_; + +const char *bond_arp_validate_to_string(BondArpValidate d) _const_; +BondArpValidate bond_arp_validate_from_string(const char *d) _pure_; + +const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_; +BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_; + +const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_; +BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 13d7b2f16..11bba2c7e 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -735,9 +735,12 @@ int boot_entries_load_config_auto( return boot_entries_load_config(esp_where, xbootldr_where, config); } -#if ENABLE_EFI -int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { - static const char * const title_table[] = { +int boot_entries_augment_from_loader( + BootConfig *config, + char **found_by_loader, + bool only_auto) { + + static const char *const title_table[] = { /* Pretty names for a few well-known automatically discovered entries. */ "auto-osx", "macOS", "auto-windows", "Windows Boot Manager", @@ -746,22 +749,14 @@ int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface", }; - _cleanup_strv_free_ char **found_by_loader = NULL; size_t n_allocated; char **i; - int r; assert(config); /* Let's add the entries discovered by the boot loader to the end of our list, unless they are * already included there. */ - r = efi_loader_get_entries(&found_by_loader); - if (IN_SET(r, -ENOENT, -EOPNOTSUPP)) - return log_debug_errno(r, "Boot loader reported no entries."); - if (r < 0) - return log_error_errno(r, "Failed to determine entries reported by boot loader: %m"); - n_allocated = config->n_entries; STRV_FOREACH(i, found_by_loader) { @@ -803,7 +798,6 @@ int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { return 0; } -#endif /********************************************************************************/ diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index b40680b64..1075a41d5 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -76,13 +76,7 @@ static inline BootEntry* boot_config_default_entry(BootConfig *config) { 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); -#if ENABLE_EFI -int boot_entries_augment_from_loader(BootConfig *config, bool only_auto); -#else -static inline int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { - return -EOPNOTSUPP; -} -#endif +int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto); static inline const char* boot_entry_title(const BootEntry *entry) { return entry->show_title ?: entry->title ?: entry->id; diff --git a/src/shared/bridge-util.c b/src/shared/bridge-util.c new file mode 100644 index 000000000..83a94ef10 --- /dev/null +++ b/src/shared/bridge-util.c @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bridge-util.h" +#include "string-table.h" + +static const char* const bridge_state_table[_NETDEV_BRIDGE_STATE_MAX] = { + [NETDEV_BRIDGE_STATE_DISABLED] = "disabled", + [NETDEV_BRIDGE_STATE_LISTENING] = "listening", + [NETDEV_BRIDGE_STATE_LEARNING] = "learning", + [NETDEV_BRIDGE_STATE_FORWARDING] = "forwarding", +}; + +DEFINE_STRING_TABLE_LOOKUP(bridge_state, BridgeState); diff --git a/src/shared/bridge-util.h b/src/shared/bridge-util.h new file mode 100644 index 000000000..5b1c3e9ea --- /dev/null +++ b/src/shared/bridge-util.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#include "conf-parser.h" + +typedef enum BridgeState { + NETDEV_BRIDGE_STATE_DISABLED = BR_STATE_DISABLED, + NETDEV_BRIDGE_STATE_LISTENING = BR_STATE_LISTENING, + NETDEV_BRIDGE_STATE_LEARNING = BR_STATE_LEARNING, + NETDEV_BRIDGE_STATE_FORWARDING = BR_STATE_FORWARDING, + NETDEV_BRIDGE_STATE_BLOCKING = BR_STATE_BLOCKING, + _NETDEV_BRIDGE_STATE_MAX, + _NETDEV_BRIDGE_STATE_INVALID = -1, +} BridgeState; + +const char *bridge_state_to_string(BridgeState d) _const_; +BridgeState bridge_state_from_string(const char *d) _pure_; diff --git a/src/shared/bus-get-properties.c b/src/shared/bus-get-properties.c new file mode 100644 index 000000000..8ad469404 --- /dev/null +++ b/src/shared/bus-get-properties.c @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-get-properties.h" +#include "rlimit-util.h" +#include "string-util.h" + +int bus_property_get_bool( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int b = *(bool*) userdata; + + return sd_bus_message_append_basic(reply, 'b', &b); +} + +int bus_property_set_bool( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + int b, r; + + r = sd_bus_message_read(value, "b", &b); + if (r < 0) + return r; + + *(bool*) userdata = b; + return 0; +} + +int bus_property_get_id128( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + sd_id128_t *id = userdata; + + if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */ + return sd_bus_message_append(reply, "ay", 0); + else + return sd_bus_message_append_array(reply, 'y', id->bytes, 16); +} + +#if __SIZEOF_SIZE_T__ != 8 +int bus_property_get_size( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t sz = *(size_t*) userdata; + + return sd_bus_message_append_basic(reply, 't', &sz); +} +#endif + +#if __SIZEOF_LONG__ != 8 +int bus_property_get_long( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int64_t l = *(long*) userdata; + + return sd_bus_message_append_basic(reply, 'x', &l); +} + +int bus_property_get_ulong( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t ul = *(unsigned long*) userdata; + + return sd_bus_message_append_basic(reply, 't', &ul); +} +#endif + +int bus_property_get_rlimit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + const char *is_soft; + struct rlimit *rl; + uint64_t u; + rlim_t x; + + assert(bus); + assert(reply); + assert(userdata); + + is_soft = endswith(property, "Soft"); + + rl = *(struct rlimit**) userdata; + if (rl) + x = is_soft ? rl->rlim_cur : rl->rlim_max; + else { + struct rlimit buf = {}; + const char *s, *p; + int z; + + /* Chop off "Soft" suffix */ + s = is_soft ? strndupa(property, is_soft - property) : property; + + /* Skip over any prefix, such as "Default" */ + assert_se(p = strstr(s, "Limit")); + + z = rlimit_from_string(p + 5); + assert(z >= 0); + + (void) getrlimit(z, &buf); + x = is_soft ? buf.rlim_cur : buf.rlim_max; + } + + /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all + * archs */ + u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; + + return sd_bus_message_append(reply, "t", u); +} diff --git a/src/shared/bus-get-properties.h b/src/shared/bus-get-properties.h new file mode 100644 index 000000000..81af74309 --- /dev/null +++ b/src/shared/bus-get-properties.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +#include "macro.h" + +int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_set_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error); +int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + +#define bus_property_get_usec ((sd_bus_property_get_t) NULL) +#define bus_property_set_usec ((sd_bus_property_set_t) NULL) + +assert_cc(sizeof(int) == sizeof(int32_t)); +#define bus_property_get_int ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(unsigned) == sizeof(uint32_t)); +#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL) + +/* On 64bit machines we can use the default serializer for size_t and + * friends, otherwise we need to cast this manually */ +#if __SIZEOF_SIZE_T__ == 8 +#define bus_property_get_size ((sd_bus_property_get_t) NULL) +#else +int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +#endif + +#if __SIZEOF_LONG__ == 8 +#define bus_property_get_long ((sd_bus_property_get_t) NULL) +#define bus_property_get_ulong ((sd_bus_property_get_t) NULL) +#else +int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +#endif + +/* uid_t and friends on Linux 32 bit. This means we can just use the + * default serializer for 32bit unsigned, for serializing it, and map + * it to NULL here */ +assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +#define bus_property_get_uid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(gid_t) == sizeof(uint32_t)); +#define bus_property_get_gid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(pid_t) == sizeof(uint32_t)); +#define bus_property_get_pid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(mode_t) == sizeof(uint32_t)); +#define bus_property_get_mode ((sd_bus_property_get_t) NULL) + +int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + +#define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val) \ + int function(sd_bus *bus, \ + const char *path, \ + const char *interface, \ + const char *property, \ + sd_bus_message *reply, \ + void *userdata, \ + sd_bus_error *error) { \ + \ + assert(bus); \ + assert(reply); \ + \ + return sd_bus_message_append(reply, bus_type, val); \ + } + +#define BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, get2) \ + int function(sd_bus *bus, \ + const char *path, \ + const char *interface, \ + const char *property, \ + sd_bus_message *reply, \ + void *userdata, \ + sd_bus_error *error) { \ + \ + data_type *data = userdata; \ + \ + assert(bus); \ + assert(reply); \ + assert(data); \ + \ + return sd_bus_message_append(reply, bus_type, \ + get2(get1(data))); \ + } + +#define ident(x) (x) +#define BUS_DEFINE_PROPERTY_GET(function, bus_type, data_type, get1) \ + BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, ident) + +#define ref(x) (*(x)) +#define BUS_DEFINE_PROPERTY_GET_REF(function, bus_type, data_type, get) \ + BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, ref, get) + +#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ + BUS_DEFINE_PROPERTY_GET_REF(function, "s", type, name##_to_string) + +#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \ + SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ + SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) diff --git a/src/shared/bus-locator.c b/src/shared/bus-locator.c new file mode 100644 index 000000000..2a5aa7467 --- /dev/null +++ b/src/shared/bus-locator.c @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-locator.h" +#include "macro.h" + +const BusLocator* const bus_home_mgr = &(BusLocator){ + .destination = "org.freedesktop.home1", + .path = "/org/freedesktop/home1", + .interface = "org.freedesktop.home1.Manager", +}; + +const BusLocator* const bus_import_mgr = &(BusLocator){ + .destination ="org.freedesktop.import1", + .path = "/org/freedesktop/import1", + .interface = "org.freedesktop.import1.Manager" +}; + +const BusLocator* const bus_locale = &(BusLocator){ + .destination = "org.freedesktop.locale1", + .path = "/org/freedesktop/locale1", + .interface = "org.freedesktop.locale1" +}; + +const BusLocator* const bus_login_mgr = &(BusLocator){ + .destination = "org.freedesktop.login1", + .path = "/org/freedesktop/login1", + .interface = "org.freedesktop.login1.Manager" +}; + +const BusLocator* const bus_machine_mgr = &(BusLocator){ + .destination ="org.freedesktop.machine1", + .path = "/org/freedesktop/machine1", + .interface = "org.freedesktop.machine1.Manager" +}; + +const BusLocator* const bus_network_mgr = &(BusLocator){ + .destination = "org.freedesktop.network1", + .path = "/org/freedesktop/network1", + .interface = "org.freedesktop.network1.Manager" +}; + +const BusLocator* const bus_portable_mgr = &(BusLocator){ + .destination = "org.freedesktop.portable1", + .path = "/org/freedesktop/portable1", + .interface = "org.freedesktop.portable1.Manager" +}; + +const BusLocator* const bus_resolve_mgr = &(BusLocator){ + .destination = "org.freedesktop.resolve1", + .path = "/org/freedesktop/resolve1", + .interface = "org.freedesktop.resolve1.Manager" +}; + +const BusLocator* const bus_systemd_mgr = &(BusLocator){ + .destination = "org.freedesktop.systemd1", + .path = "/org/freedesktop/systemd1", + .interface = "org.freedesktop.systemd1.Manager" +}; + +const BusLocator* const bus_timedate = &(BusLocator){ + .destination = "org.freedesktop.timedate1", + .path = "/org/freedesktop/timedate1", + .interface = "org.freedesktop.timedate1" +}; + +/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated + * within a single struct. */ +int bus_call_method_async( + sd_bus *bus, + sd_bus_slot **slot, + const BusLocator *locator, + const char *member, + sd_bus_message_handler_t callback, + void *userdata, + const char *types, ...) { + + va_list ap; + int r; + + assert(locator); + + va_start(ap, types); + r = sd_bus_call_method_asyncv(bus, slot, locator->destination, locator->path, locator->interface, member, callback, userdata, types, ap); + va_end(ap); + + return r; +} + +int bus_call_method( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, + const char *types, ...) { + + va_list ap; + int r; + + assert(locator); + + va_start(ap, types); + r = sd_bus_call_methodv(bus, locator->destination, locator->path, locator->interface, member, error, reply, types, ap); + va_end(ap); + + return r; +} + +int bus_get_property( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, + const char *type) { + + assert(locator); + + return sd_bus_get_property(bus, locator->destination, locator->path, locator->interface, member, error, reply, type); +} + +int bus_get_property_trivial( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + char type, void *ptr) { + + assert(locator); + + return sd_bus_get_property_trivial(bus, locator->destination, locator->path, locator->interface, member, error, type, ptr); +} + +int bus_get_property_string( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + char **ret) { + + assert(locator); + + return sd_bus_get_property_string(bus, locator->destination, locator->path, locator->interface, member, error, ret); +} + +int bus_get_property_strv( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + char ***ret) { + + assert(locator); + + return sd_bus_get_property_strv(bus, locator->destination, locator->path, locator->interface, member, error, ret); +} + +int bus_set_property( + sd_bus *bus, + const BusLocator *locator, + const char *member, + sd_bus_error *error, + const char *type, ...) { + + va_list ap; + int r; + + assert(locator); + + va_start(ap, type); + r = sd_bus_set_propertyv(bus, locator->destination, locator->path, locator->interface, member, error, type, ap); + va_end(ap); + + return r; +} + +int bus_match_signal( + sd_bus *bus, + sd_bus_slot **ret, + const BusLocator *locator, + const char *member, + sd_bus_message_handler_t callback, + void *userdata) { + + assert(locator); + + return sd_bus_match_signal(bus, ret, locator->destination, locator->path, locator->interface, member, callback, userdata); +} + +int bus_match_signal_async( + sd_bus *bus, + sd_bus_slot **ret, + const BusLocator *locator, + const char *member, + sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, + void *userdata) { + + assert(locator); + + return sd_bus_match_signal_async(bus, ret, locator->destination, locator->path, locator->interface, member, callback, install_callback, userdata); +} + +int bus_message_new_method_call( + sd_bus *bus, + sd_bus_message **m, + const BusLocator *locator, + const char *member) { + + assert(locator); + + return sd_bus_message_new_method_call(bus, m, locator->destination, locator->path, locator->interface, member); +} diff --git a/src/shared/bus-locator.h b/src/shared/bus-locator.h new file mode 100644 index 000000000..2b892360b --- /dev/null +++ b/src/shared/bus-locator.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +typedef struct BusLocator { + const char *destination; + const char *path; + const char *interface; +} BusLocator; + +extern const BusLocator* const bus_home_mgr; +extern const BusLocator* const bus_import_mgr; +extern const BusLocator* const bus_locale; +extern const BusLocator* const bus_login_mgr; +extern const BusLocator* const bus_machine_mgr; +extern const BusLocator* const bus_network_mgr; +extern const BusLocator* const bus_portable_mgr; +extern const BusLocator* const bus_resolve_mgr; +extern const BusLocator* const bus_systemd_mgr; +extern const BusLocator* const bus_timedate; + +/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated + * within a single struct. */ +int bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); +int bus_call_method(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *types, ...); +int bus_get_property(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *type); +int bus_get_property_trivial(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char type, void *ptr); +int bus_get_property_string(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char **ret); +int bus_get_property_strv(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char ***ret); +int bus_set_property(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, const char *type, ...); +int bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, void *userdata); +int bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata); +int bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const BusLocator *locator, const char *member); diff --git a/src/shared/bus-log-control-api.c b/src/shared/bus-log-control-api.c new file mode 100644 index 000000000..7c487ada9 --- /dev/null +++ b/src/shared/bus-log-control-api.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "bus-get-properties.h" +#include "bus-log-control-api.h" +#include "bus-util.h" +#include "log.h" +#include "sd-bus.h" +#include "syslog-util.h" + +int bus_property_get_log_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *t = NULL; + int r; + + assert(bus); + assert(reply); + + r = log_level_to_string_alloc(log_get_max_level(), &t); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "s", t); +} + +int bus_property_set_log_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + const char *t; + int r; + + assert(bus); + assert(value); + + r = sd_bus_message_read(value, "s", &t); + if (r < 0) + return r; + + r = log_level_from_string(t); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log level '%s'", t); + + log_info("Setting log level to %s.", t); + log_set_max_level(r); + + return 0; +} + +BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_log_target, "s", log_target_to_string(log_get_target())); + +int bus_property_set_log_target( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + LogTarget target; + const char *t; + int r; + + assert(bus); + assert(value); + + r = sd_bus_message_read(value, "s", &t); + if (r < 0) + return r; + + target = log_target_from_string(t); + if (target < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log target '%s'", t); + + log_info("Setting log target to %s.", log_target_to_string(target)); + log_set_target(target); + log_open(); + + return 0; +} + +BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_syslog_identifier, "s", program_invocation_short_name); + +static const sd_bus_vtable log_control_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, bus_property_set_log_level, 0, 0), + SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, bus_property_set_log_target, 0, 0), + SD_BUS_PROPERTY("SyslogIdentifier", "s", bus_property_get_syslog_identifier, 0, 0), + + /* One of those days we might want to add a similar, second interface to cover common service + * operations such as Reload(), Reexecute(), Exit() … and maybe some properties exposing version + * number and other meta-data of the service. */ + + SD_BUS_VTABLE_END, +}; + +const BusObjectImplementation log_control_object = { + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + .vtables = BUS_VTABLES(log_control_vtable), +}; diff --git a/src/shared/bus-log-control-api.h b/src/shared/bus-log-control-api.h new file mode 100644 index 000000000..64eaa540f --- /dev/null +++ b/src/shared/bus-log-control-api.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +#include "bus-object.h" + +extern const BusObjectImplementation log_control_object; +static inline int bus_log_control_api_register(sd_bus *bus) { + return bus_add_implementation(bus, &log_control_object, NULL); +} + +int bus_property_get_log_level(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_set_log_level(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error); + +int bus_property_get_log_target(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_set_log_target(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + +int bus_property_get_syslog_identifier(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); diff --git a/src/shared/bus-map-properties.c b/src/shared/bus-map-properties.c new file mode 100644 index 000000000..ab393c395 --- /dev/null +++ b/src/shared/bus-map-properties.c @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-map-properties.h" +#include "alloc-util.h" +#include "strv.h" +#include "bus-message.h" + +int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + sd_id128_t *p = userdata; + const void *v; + size_t n; + int r; + + r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n); + if (r < 0) + return r; + + if (n == 0) + *p = SD_ID128_NULL; + else if (n == 16) + memcpy((*p).bytes, v, n); + else + return -EINVAL; + + return 0; +} + +static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) { + char type; + int r; + + r = sd_bus_message_peek_type(m, &type, NULL); + if (r < 0) + return r; + + switch (type) { + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: { + const char **p = userdata; + const char *s; + + r = sd_bus_message_read_basic(m, type, &s); + if (r < 0) + return r; + + if (isempty(s)) + s = NULL; + + if (flags & BUS_MAP_STRDUP) + return free_and_strdup((char **) userdata, s); + + *p = s; + return 0; + } + + case SD_BUS_TYPE_ARRAY: { + _cleanup_strv_free_ char **l = NULL; + char ***p = userdata; + + r = bus_message_read_strv_extend(m, &l); + if (r < 0) + return r; + + return strv_extend_strv(p, l, false); + } + + case SD_BUS_TYPE_BOOLEAN: { + int b; + + r = sd_bus_message_read_basic(m, type, &b); + if (r < 0) + return r; + + if (flags & BUS_MAP_BOOLEAN_AS_BOOL) + *(bool*) userdata = b; + else + *(int*) userdata = b; + + return 0; + } + + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: { + uint32_t u, *p = userdata; + + r = sd_bus_message_read_basic(m, type, &u); + if (r < 0) + return r; + + *p = u; + return 0; + } + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: { + uint64_t t, *p = userdata; + + r = sd_bus_message_read_basic(m, type, &t); + if (r < 0) + return r; + + *p = t; + return 0; + } + + case SD_BUS_TYPE_DOUBLE: { + double d, *p = userdata; + + r = sd_bus_message_read_basic(m, type, &d); + if (r < 0) + return r; + + *p = d; + return 0; + }} + + return -EOPNOTSUPP; +} + +int bus_message_map_all_properties( + sd_bus_message *m, + const struct bus_properties_map *map, + unsigned flags, + sd_bus_error *error, + void *userdata) { + + int r; + + assert(m); + assert(map); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const struct bus_properties_map *prop; + const char *member; + const char *contents; + void *v; + unsigned i; + + r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member); + if (r < 0) + return r; + + for (i = 0, prop = NULL; map[i].member; i++) + if (streq(map[i].member, member)) { + prop = &map[i]; + break; + } + + if (prop) { + r = sd_bus_message_peek_type(m, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return r; + + v = (uint8_t *)userdata + prop->offset; + if (map[i].set) + r = prop->set(sd_bus_message_get_bus(m), member, m, error, v); + else + r = map_basic(sd_bus_message_get_bus(m), member, m, flags, error, v); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } else { + r = sd_bus_message_skip(m, "v"); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + if (r < 0) + return r; + + return sd_bus_message_exit_container(m); +} + +int bus_map_all_properties( + sd_bus *bus, + const char *destination, + const char *path, + const struct bus_properties_map *map, + unsigned flags, + sd_bus_error *error, + sd_bus_message **reply, + void *userdata) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + assert(destination); + assert(path); + assert(map); + assert(reply || (flags & BUS_MAP_STRDUP)); + + r = sd_bus_call_method( + bus, + destination, + path, + "org.freedesktop.DBus.Properties", + "GetAll", + error, + &m, + "s", ""); + if (r < 0) + return r; + + r = bus_message_map_all_properties(m, map, flags, error, userdata); + if (r < 0) + return r; + + if (reply) + *reply = sd_bus_message_ref(m); + + return r; +} diff --git a/src/shared/bus-map-properties.h b/src/shared/bus-map-properties.h new file mode 100644 index 000000000..11b899227 --- /dev/null +++ b/src/shared/bus-map-properties.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); + +struct bus_properties_map { + const char *member; + const char *signature; + bus_property_set_t set; + size_t offset; +}; + +enum { + BUS_MAP_STRDUP = 1 << 0, /* If set, each "s" message is duplicated. Thus, each pointer needs to be freed. */ + BUS_MAP_BOOLEAN_AS_BOOL = 1 << 1, /* If set, each "b" message is written to a bool pointer. If not set, "b" is written to a int pointer. */ +}; + +int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); + +int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata); +int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, + unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata); diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c new file mode 100644 index 000000000..85e1e98e1 --- /dev/null +++ b/src/shared/bus-message-util.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-message-util.h" + +#include "resolve-util.h" + +int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) { + int ifindex, r; + + assert(message); + assert(ret); + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(message, "i", &ifindex); + if (r < 0) + return r; + + if (ifindex <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); + + *ret = ifindex; + + return 0; +} + +int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret) { + int family, r; + + assert(message); + assert(ret); + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(message, "i", &family); + if (r < 0) + return r; + + if (!IN_SET(family, AF_INET, AF_INET6)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); + + *ret = family; + return 0; +} + +int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr) { + int family, r; + const void *d; + size_t sz; + + assert(message); + + r = sd_bus_message_read(message, "i", &family); + if (r < 0) + return r; + + r = sd_bus_message_read_array(message, 'y', &d, &sz); + if (r < 0) + return r; + + if (!IN_SET(family, AF_INET, AF_INET6)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); + + if (sz != FAMILY_ADDRESS_SIZE(family)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); + + if (ret_family) + *ret_family = family; + if (ret_addr) + memcpy(ret_addr, d, sz); + return 0; +} + +static int bus_message_read_dns_one( + sd_bus_message *message, + sd_bus_error *error, + bool extended, + int *ret_family, + union in_addr_union *ret_address, + uint16_t *ret_port, + const char **ret_server_name) { + const char *server_name = NULL; + union in_addr_union a; + uint16_t port = 0; + int family, r; + + assert(message); + assert(ret_family); + assert(ret_address); + assert(ret_port); + assert(ret_server_name); + + r = sd_bus_message_enter_container(message, 'r', extended ? "iayqs" : "iay"); + if (r <= 0) + return r; + + r = bus_message_read_in_addr_auto(message, error, &family, &a); + if (r < 0) + return r; + + if (!dns_server_address_valid(family, &a)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address"); + + if (extended) { + r = sd_bus_message_read(message, "q", &port); + if (r < 0) + return r; + + if (IN_SET(port, 53, 853)) + port = 0; + + r = sd_bus_message_read(message, "s", &server_name); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + *ret_family = family; + *ret_address = a; + *ret_port = port; + *ret_server_name = server_name; + + return 1; +} + +int bus_message_read_dns_servers( + sd_bus_message *message, + sd_bus_error *error, + bool extended, + struct in_addr_full ***ret_dns, + size_t *ret_n_dns) { + + struct in_addr_full **dns = NULL; + size_t n = 0, allocated = 0; + int r; + + assert(message); + assert(ret_dns); + assert(ret_n_dns); + + r = sd_bus_message_enter_container(message, 'a', extended ? "(iayqs)" : "(iay)"); + if (r < 0) + return r; + + for (;;) { + const char *server_name; + union in_addr_union a; + uint16_t port; + int family; + + r = bus_message_read_dns_one(message, error, extended, &family, &a, &port, &server_name); + if (r < 0) + goto clear; + if (r == 0) + break; + + if (!GREEDY_REALLOC(dns, allocated, n+1)) { + r = -ENOMEM; + goto clear; + } + + r = in_addr_full_new(family, &a, port, 0, server_name, dns + n); + if (r < 0) + goto clear; + + n++; + } + + *ret_dns = TAKE_PTR(dns); + *ret_n_dns = n; + return 0; + +clear: + for (size_t i = 0; i < n; i++) + in_addr_full_free(dns[i]); + free(dns); + + return r; +} diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h new file mode 100644 index 000000000..98ad035ee --- /dev/null +++ b/src/shared/bus-message-util.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +#include "in-addr-util.h" +#include "socket-netlink.h" + +int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret); +int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret); +int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr); + +int bus_message_read_dns_servers( + sd_bus_message *message, + sd_bus_error *error, + bool extended, + struct in_addr_full ***ret_dns, + size_t *ret_n_dns); diff --git a/src/shared/bus-object.c b/src/shared/bus-object.c new file mode 100644 index 000000000..217d43d33 --- /dev/null +++ b/src/shared/bus-object.c @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-introspect.h" +#include "bus-object.h" +#include "macro.h" +#include "string-util.h" +#include "strv.h" + +int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, void *userdata) { + int r; + + log_debug("Registering bus object implementation for path=%s iface=%s", impl->path, impl->interface); + + for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) { + r = sd_bus_add_object_vtable(bus, NULL, + impl->path, + impl->interface, + *p, + userdata); + if (r < 0) + return log_error_errno(r, "Failed to register bus path %s with interface %s: %m", + impl->path, + impl->interface); + } + + for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) { + r = sd_bus_add_fallback_vtable(bus, NULL, + impl->path, + impl->interface, + p->vtable, + p->object_find, + userdata); + if (r < 0) + return log_error_errno(r, "Failed to register bus path %s with interface %s: %m", + impl->path, + impl->interface); + } + + if (impl->node_enumerator) { + r = sd_bus_add_node_enumerator(bus, NULL, + impl->path, + impl->node_enumerator, + userdata); + if (r < 0) + return log_error_errno(r, "Failed to add node enumerator for %s: %m", + impl->path); + } + + if (impl->manager) { + r = sd_bus_add_object_manager(bus, NULL, impl->path); + if (r < 0) + return log_error_errno(r, "Failed to add object manager for %s: %m", impl->path); + } + + for (size_t i = 0; impl->children && impl->children[i]; i++) { + r = bus_add_implementation(bus, impl->children[i], userdata); + if (r < 0) + return r; + } + + return 0; +} + +static const BusObjectImplementation* find_implementation( + const char *pattern, + const BusObjectImplementation* const* bus_objects) { + + for (size_t i = 0; bus_objects && bus_objects[i]; i++) { + const BusObjectImplementation *impl = bus_objects[i]; + + if (STR_IN_SET(pattern, impl->path, impl->interface)) + return impl; + + impl = find_implementation(pattern, impl->children); + if (impl) + return impl; + } + + return NULL; +} + +static int bus_introspect_implementation( + struct introspect *intro, + const BusObjectImplementation *impl) { + int r; + + for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) { + r = introspect_write_interface(intro, impl->interface, *p); + if (r < 0) + return log_error_errno(r, "Failed to write introspection data: %m"); + } + + for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) { + r = introspect_write_interface(intro, impl->interface, p->vtable); + if (r < 0) + return log_error_errno(r, "Failed to write introspection data: %m"); + } + + return 0; +} + +static void list_paths( + FILE *out, + const BusObjectImplementation* const* bus_objects) { + + for (size_t i = 0; bus_objects[i]; i++) { + fprintf(out, "%s\t%s\n", bus_objects[i]->path, bus_objects[i]->interface); + if (bus_objects[i]->children) + list_paths(out, bus_objects[i]->children); + } +} + +int bus_introspect_implementations( + FILE *out, + const char *pattern, + const BusObjectImplementation* const* bus_objects) { + + const BusObjectImplementation *impl, *main_impl = NULL; + _cleanup_free_ char *s = NULL; + int r; + + if (streq(pattern, "list")) { + list_paths(out, bus_objects); + return 0; + } + + struct introspect intro = {}; + bool is_interface = sd_bus_interface_name_is_valid(pattern); + + impl = find_implementation(pattern, bus_objects); + if (!impl) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "%s %s not found", + is_interface ? "Interface" : "Object path", + pattern); + + /* We use trusted=false here to get all the @org.freedesktop.systemd1.Privileged annotations. */ + r = introspect_begin(&intro, false); + if (r < 0) + return log_error_errno(r, "Failed to write introspection data: %m"); + + r = introspect_write_default_interfaces(&intro, impl->manager); + if (r < 0) + return log_error_errno(r, "Failed to write introspection data: %m"); + + /* Check if there is a non-fallback path that applies to the given interface, also + * print it. This is useful in the case of units: o.fd.systemd1.Service is declared + * as a fallback vtable for o/fd/systemd1/unit, and we also want to print + * o.fd.systemd1.Unit, which is the non-fallback implementation. */ + if (impl->fallback_vtables && is_interface) + main_impl = find_implementation(impl->path, bus_objects); + + if (main_impl) + bus_introspect_implementation(&intro, main_impl); + + if (impl != main_impl) + bus_introspect_implementation(&intro, impl); + + _cleanup_set_free_ Set *nodes = NULL; + + for (size_t i = 0; impl->children && impl->children[i]; i++) { + r = set_put_strdup(&nodes, impl->children[i]->path); + if (r < 0) + return log_oom(); + } + + r = introspect_write_child_nodes(&intro, nodes, impl->path); + if (r < 0) + return r; + + r = introspect_finish(&intro, &s); + if (r < 0) + return log_error_errno(r, "Failed to write introspection data: %m"); + + fputs(s, out); + return 0; +} diff --git a/src/shared/bus-object.h b/src/shared/bus-object.h new file mode 100644 index 000000000..8add854e5 --- /dev/null +++ b/src/shared/bus-object.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#include "sd-bus.h" + +typedef struct BusObjectImplementation BusObjectImplementation; + +typedef struct BusObjectVtablePair { + const sd_bus_vtable *vtable; + sd_bus_object_find_t object_find; +} BusObjectVtablePair; + +struct BusObjectImplementation { + const char *path; + const char *interface; + const sd_bus_vtable **vtables; + const BusObjectVtablePair *fallback_vtables; + sd_bus_node_enumerator_t node_enumerator; + bool manager; + const BusObjectImplementation **children; +}; + +#define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL }) +#define BUS_FALLBACK_VTABLES(...) ((const BusObjectVtablePair[]) { __VA_ARGS__, {} }) +#define BUS_IMPLEMENTATIONS(...) ((const BusObjectImplementation* []) { __VA_ARGS__, NULL }) + +int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, void *userdata); +int bus_introspect_implementations( + FILE *out, + const char *pattern, + const BusObjectImplementation* const* bus_objects); diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c new file mode 100644 index 000000000..bf7d050fa --- /dev/null +++ b/src/shared/bus-print-properties.c @@ -0,0 +1,462 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-print-properties.h" +#include "cap-list.h" +#include "cgroup-util.h" +#include "escape.h" +#include "mountpoint-util.h" +#include "nsflags.h" +#include "parse-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "time-util.h" +#include "user-util.h" + +int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value) { + assert(name); + + if (expected_value && !streq_ptr(expected_value, value)) + return 0; + + if (only_value) + puts(value); + else + printf("%s=%s\n", name, value); + + return 0; +} + +int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) { + va_list ap; + int r; + + assert(name); + assert(fmt); + + if (expected_value) { + _cleanup_free_ char *s = NULL; + + va_start(ap, fmt); + r = vasprintf(&s, fmt, ap); + va_end(ap); + if (r < 0) + return -ENOMEM; + + if (streq_ptr(expected_value, s)) { + if (only_value) + puts(s); + else + printf("%s=%s\n", name, s); + } + + return 0; + } + + if (!only_value) + printf("%s=", name); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + puts(""); + + return 0; +} + +static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) { + char type; + const char *contents; + int r; + + assert(name); + assert(m); + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + + switch (type) { + + case SD_BUS_TYPE_STRING: { + const char *s; + + r = sd_bus_message_read_basic(m, type, &s); + if (r < 0) + return r; + + if (all || !isempty(s)) { + bool good; + + /* This property has a single value, so we need to take + * care not to print a new line, everything else is OK. */ + good = !strchr(s, '\n'); + bus_print_property_value(name, expected_value, value, good ? s : "[unprintable]"); + } + + return 1; + } + + case SD_BUS_TYPE_BOOLEAN: { + int b; + + r = sd_bus_message_read_basic(m, type, &b); + if (r < 0) + return r; + + if (expected_value && parse_boolean(expected_value) != b) + return 1; + + bus_print_property_value(name, NULL, value, yes_no(b)); + return 1; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t u; + + r = sd_bus_message_read_basic(m, type, &u); + if (r < 0) + return r; + + /* Yes, heuristics! But we can change this check + * should it turn out to not be sufficient */ + + if (endswith(name, "Timestamp") || + STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec")) { + char timestamp[FORMAT_TIMESTAMP_MAX]; + const char *t; + + t = format_timestamp(timestamp, sizeof(timestamp), u); + if (t || all) + bus_print_property_value(name, expected_value, value, strempty(t)); + + } else if (strstr(name, "USec")) { + char timespan[FORMAT_TIMESPAN_MAX]; + + (void) format_timespan(timespan, sizeof(timespan), u, 0); + bus_print_property_value(name, expected_value, value, timespan); + + } else if (streq(name, "CoredumpFilter")) { + char buf[STRLEN("0xFFFFFFFF")]; + + xsprintf(buf, "0x%"PRIx64, u); + bus_print_property_value(name, expected_value, value, buf); + + } else if (streq(name, "RestrictNamespaces")) { + _cleanup_free_ char *s = NULL; + const char *result; + + if ((u & NAMESPACE_FLAGS_ALL) == 0) + result = "yes"; + else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL)) + result = "no"; + else { + r = namespace_flags_to_string(u, &s); + if (r < 0) + return r; + + result = strempty(s); + } + + bus_print_property_value(name, expected_value, value, result); + + } else if (streq(name, "MountFlags")) { + const char *result; + + result = mount_propagation_flags_to_string(u); + if (!result) + return -EINVAL; + + bus_print_property_value(name, expected_value, value, result); + + } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { + _cleanup_free_ char *s = NULL; + + r = capability_set_to_string_alloc(u, &s); + if (r < 0) + return r; + + bus_print_property_value(name, expected_value, value, s); + + } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) || + (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) || + (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) || + (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) || + (endswith(name, "NSec") && u == (uint64_t) -1)) + + bus_print_property_value(name, expected_value, value, "[not set]"); + + else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || + (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) || + (startswith(name, "Limit") && u == (uint64_t) -1) || + (startswith(name, "DefaultLimit") && u == (uint64_t) -1)) + + bus_print_property_value(name, expected_value, value, "infinity"); + else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == (uint64_t) -1) + bus_print_property_value(name, expected_value, value, "[no data]"); + else + bus_print_property_valuef(name, expected_value, value, "%"PRIu64, u); + + return 1; + } + + case SD_BUS_TYPE_INT64: { + int64_t i; + + r = sd_bus_message_read_basic(m, type, &i); + if (r < 0) + return r; + + bus_print_property_valuef(name, expected_value, value, "%"PRIi64, i); + return 1; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t u; + + r = sd_bus_message_read_basic(m, type, &u); + if (r < 0) + return r; + + if (strstr(name, "UMask") || strstr(name, "Mode")) + bus_print_property_valuef(name, expected_value, value, "%04o", u); + + else if (streq(name, "UID")) { + if (u == UID_INVALID) + bus_print_property_value(name, expected_value, value, "[not set]"); + else + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); + } else if (streq(name, "GID")) { + if (u == GID_INVALID) + bus_print_property_value(name, expected_value, value, "[not set]"); + else + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); + } else + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); + + return 1; + } + + case SD_BUS_TYPE_INT32: { + int32_t i; + + r = sd_bus_message_read_basic(m, type, &i); + if (r < 0) + return r; + + bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i); + return 1; + } + + case SD_BUS_TYPE_DOUBLE: { + double d; + + r = sd_bus_message_read_basic(m, type, &d); + if (r < 0) + return r; + + bus_print_property_valuef(name, expected_value, value, "%g", d); + return 1; + } + + case SD_BUS_TYPE_ARRAY: + if (streq(contents, "s")) { + bool first = true; + const char *str; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) { + _cleanup_free_ char *e = NULL; + + e = shell_maybe_quote(str, ESCAPE_BACKSLASH_ONELINE); + if (!e) + return -ENOMEM; + + if (first) { + if (!value) + printf("%s=", name); + first = false; + } else + fputs(" ", stdout); + + fputs(e, stdout); + } + if (r < 0) + return r; + + if (first && all && !value) + printf("%s=", name); + if (!first || all) + puts(""); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 1; + + } else if (streq(contents, "y")) { + const uint8_t *u; + size_t n; + + r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n); + if (r < 0) + return r; + + if (all || n > 0) { + unsigned i; + + if (!value) + printf("%s=", name); + + for (i = 0; i < n; i++) + printf("%02x", u[i]); + + puts(""); + } + + return 1; + + } else if (streq(contents, "u")) { + uint32_t *u; + size_t n; + + r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n); + if (r < 0) + return r; + + if (all || n > 0) { + unsigned i; + + if (!value) + printf("%s=", name); + + for (i = 0; i < n; i++) + printf("%08x", u[i]); + + puts(""); + } + + return 1; + } + + break; + } + + return 0; +} + +int bus_message_print_all_properties( + sd_bus_message *m, + bus_message_print_t func, + char **filter, + bool value, + bool all, + Set **found_properties) { + + int r; + + assert(m); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + _cleanup_free_ char *name_with_equal = NULL; + const char *name, *contents, *expected_value = NULL; + + r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); + if (r < 0) + return r; + + if (found_properties) { + r = set_ensure_put(found_properties, &string_hash_ops, name); + if (r < 0) + return log_oom(); + } + + name_with_equal = strjoin(name, "="); + if (!name_with_equal) + return log_oom(); + + if (!filter || strv_find(filter, name) || + (expected_value = strv_find_startswith(filter, name_with_equal))) { + r = sd_bus_message_peek_type(m, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return r; + + if (func) + r = func(name, expected_value, m, value, all); + if (!func || r == 0) + r = bus_print_property(name, expected_value, m, value, all); + if (r < 0) + return r; + if (r == 0) { + if (all && !expected_value) + printf("%s=[unprintable]\n", name); + /* skip what we didn't read */ + r = sd_bus_message_skip(m, contents); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } else { + r = sd_bus_message_skip(m, "v"); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +int bus_print_all_properties( + sd_bus *bus, + const char *dest, + const char *path, + bus_message_print_t func, + char **filter, + bool value, + bool all, + Set **found_properties) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(path); + + r = sd_bus_call_method(bus, + dest, + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return r; + + return bus_message_print_all_properties(reply, func, filter, value, all, found_properties); +} diff --git a/src/shared/bus-print-properties.h b/src/shared/bus-print-properties.h new file mode 100644 index 000000000..1c21dff2c --- /dev/null +++ b/src/shared/bus-print-properties.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "sd-bus.h" + +#include "macro.h" +#include "set.h" + +typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all); + +int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value); +int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5); +int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); +int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index a30876c1a..f2652ed9a 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -8,10 +8,12 @@ #include "cgroup-setup.h" #include "cgroup-util.h" #include "condition.h" +#include "coredump-util.h" #include "cpu-set-util.h" #include "escape.h" #include "exec-util.h" #include "exit-status.h" +#include "fileio.h" #include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" @@ -23,6 +25,7 @@ #include "nsflags.h" #include "numa-util.h" #include "parse-util.h" +#include "path-util.h" #include "process-util.h" #include "rlimit-util.h" #include "securebits-util.h" @@ -119,6 +122,7 @@ DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64); DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode); DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou); DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string); static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) { int r; @@ -487,11 +491,24 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons "MemoryLimit", "TasksMax")) { - if (isempty(eq) || streq(eq, "infinity")) { + if (streq(eq, "infinity")) { r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); if (r < 0) return bus_log_create_error(r); return 1; + } else if (isempty(eq)) { + uint64_t empty_value = STR_IN_SET(field, + "DefaultMemoryLow", + "DefaultMemoryMin", + "MemoryLow", + "MemoryMin") ? + CGROUP_LIMIT_MIN : + CGROUP_LIMIT_MAX; + + r = sd_bus_message_append(m, "(sv)", field, "t", empty_value); + if (r < 0) + return bus_log_create_error(r); + return 1; } r = parse_permille(eq); @@ -832,6 +849,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "ProtectHome", "SELinuxContext", "RootImage", + "RootVerity", "RuntimeDirectoryPreserve", "Personality", "KeyringMode", @@ -898,6 +916,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "OOMScoreAdjust")) return bus_append_safe_atoi(m, field, eq); + if (streq(field, "CoredumpFilter")) + return bus_append_coredump_filter_mask_from_string(m, field, eq); + if (streq(field, "Nice")) return bus_append_parse_nice(m, field, eq); @@ -1152,11 +1173,11 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { - int whitelist = 1; + int allow_list = 1; const char *p = eq; if (*p == '~') { - whitelist = 0; + allow_list = 0; p++; } @@ -1176,7 +1197,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_basic(m, 'b', &whitelist); + r = sd_bus_message_append_basic(m, 'b', &allow_list); if (r < 0) return bus_log_create_error(r); @@ -1395,6 +1416,44 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con return 1; } + if (streq(field, "RootHash")) { + _cleanup_free_ void *roothash_decoded = NULL; + size_t roothash_decoded_size = 0; + + /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */ + if (path_is_absolute(eq)) + return bus_append_string(m, "RootHashPath", eq); + + /* We have a roothash to decode, eg: RootHash=012345789abcdef */ + r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size); + if (r < 0) + return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq); + if (roothash_decoded_size < sizeof(sd_id128_t)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq); + + return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size); + } + + if (streq(field, "RootHashSignature")) { + _cleanup_free_ void *roothash_sig_decoded = NULL; + char *value; + size_t roothash_sig_decoded_size = 0; + + /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */ + if (path_is_absolute(eq)) + return bus_append_string(m, "RootHashSignaturePath", eq); + + if (!(value = startswith(eq, "base64:"))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq); + + /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */ + r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size); + if (r < 0) + return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq); + + return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size); + } + return 0; } @@ -1431,7 +1490,8 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", - "ForceUnmount")) + "ForceUnmount", + "ReadwriteOnly")) return bus_append_parse_boolean(m, field, eq); return 0; @@ -1484,7 +1544,9 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con "NotifyAccess", "USBFunctionDescriptors", "USBFunctionStrings", - "OOMPolicy")) + "OOMPolicy", + "TimeoutStartFailureMode", + "TimeoutStopFailureMode")) return bus_append_string(m, field, eq); if (STR_IN_SET(field, "PermissionsStartOnly", @@ -1626,6 +1688,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons "Broadcast", "PassCredentials", "PassSecurity", + "PassPacketInfo", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 8e6a6e2ce..77c1c6218 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -14,22 +14,14 @@ #include "sd-event.h" #include "sd-id128.h" -#include "alloc-util.h" +/* #include "alloc-util.h" */ #include "bus-internal.h" #include "bus-label.h" -#include "bus-message.h" #include "bus-util.h" -#include "cap-list.h" -#include "cgroup-util.h" -#include "mountpoint-util.h" -#include "nsflags.h" -#include "parse-util.h" #include "path-util.h" -#include "rlimit-util.h" #include "socket-util.h" #include "stdio-util.h" -#include "strv.h" -#include "user-util.h" +/* #include "string-util.h" */ static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; @@ -115,36 +107,16 @@ int bus_event_loop_with_idle( return r; if (r == 0 && !exiting && idle) { + /* Inform the service manager that we are going down, so that it will queue all + * further start requests, instead of assuming we are already running. */ + sd_notify(false, "STOPPING=1"); - r = sd_bus_try_close(bus); - if (r == -EBUSY) - continue; - - /* Fallback for dbus1 connections: we - * unregister the name and wait for the - * response to come through for it */ - if (r == -EOPNOTSUPP) { - - /* Inform the service manager that we - * are going down, so that it will - * queue all further start requests, - * instead of assuming we are already - * running. */ - sd_notify(false, "STOPPING=1"); - - r = bus_async_unregister_and_exit(e, bus, name); - if (r < 0) - return r; - - exiting = true; - continue; - } - + r = bus_async_unregister_and_exit(e, bus, name); if (r < 0) return r; - sd_event_exit(e, 0); - break; + exiting = true; + continue; } } @@ -271,673 +243,6 @@ int bus_connect_user_systemd(sd_bus **_bus) { return 0; } -int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value) { - assert(name); - - if (expected_value && !streq_ptr(expected_value, value)) - return 0; - - if (only_value) - puts(value); - else - printf("%s=%s\n", name, value); - - return 0; -} - -int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) { - va_list ap; - int r; - - assert(name); - assert(fmt); - - if (expected_value) { - _cleanup_free_ char *s = NULL; - - va_start(ap, fmt); - r = vasprintf(&s, fmt, ap); - va_end(ap); - if (r < 0) - return -ENOMEM; - - if (streq_ptr(expected_value, s)) { - if (only_value) - puts(s); - else - printf("%s=%s\n", name, s); - } - - return 0; - } - - if (!only_value) - printf("%s=", name); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - puts(""); - - return 0; -} - -static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) { - char type; - const char *contents; - int r; - - assert(name); - assert(m); - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - - switch (type) { - - case SD_BUS_TYPE_STRING: { - const char *s; - - r = sd_bus_message_read_basic(m, type, &s); - if (r < 0) - return r; - - if (all || !isempty(s)) { - bool good; - - /* This property has a single value, so we need to take - * care not to print a new line, everything else is OK. */ - good = !strchr(s, '\n'); - bus_print_property_value(name, expected_value, value, good ? s : "[unprintable]"); - } - - return 1; - } - - case SD_BUS_TYPE_BOOLEAN: { - int b; - - r = sd_bus_message_read_basic(m, type, &b); - if (r < 0) - return r; - - if (expected_value && parse_boolean(expected_value) != b) - return 1; - - bus_print_property_value(name, NULL, value, yes_no(b)); - return 1; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t u; - - r = sd_bus_message_read_basic(m, type, &u); - if (r < 0) - return r; - - /* Yes, heuristics! But we can change this check - * should it turn out to not be sufficient */ - - if (endswith(name, "Timestamp") || - STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec")) { - char timestamp[FORMAT_TIMESTAMP_MAX]; - const char *t; - - t = format_timestamp(timestamp, sizeof(timestamp), u); - if (t || all) - bus_print_property_value(name, expected_value, value, strempty(t)); - - } else if (strstr(name, "USec")) { - char timespan[FORMAT_TIMESPAN_MAX]; - - (void) format_timespan(timespan, sizeof(timespan), u, 0); - bus_print_property_value(name, expected_value, value, timespan); - - } else if (streq(name, "RestrictNamespaces")) { - _cleanup_free_ char *s = NULL; - const char *result; - - if ((u & NAMESPACE_FLAGS_ALL) == 0) - result = "yes"; - else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL)) - result = "no"; - else { - r = namespace_flags_to_string(u, &s); - if (r < 0) - return r; - - result = strempty(s); - } - - bus_print_property_value(name, expected_value, value, result); - - } else if (streq(name, "MountFlags")) { - const char *result; - - result = mount_propagation_flags_to_string(u); - if (!result) - return -EINVAL; - - bus_print_property_value(name, expected_value, value, result); - - } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { - _cleanup_free_ char *s = NULL; - - r = capability_set_to_string_alloc(u, &s); - if (r < 0) - return r; - - bus_print_property_value(name, expected_value, value, s); - - } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) || - (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) || - (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) || - (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) || - (endswith(name, "NSec") && u == (uint64_t) -1)) - - bus_print_property_value(name, expected_value, value, "[not set]"); - - else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || - (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) || - (startswith(name, "Limit") && u == (uint64_t) -1) || - (startswith(name, "DefaultLimit") && u == (uint64_t) -1)) - - bus_print_property_value(name, expected_value, value, "infinity"); - else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == (uint64_t) -1) - bus_print_property_value(name, expected_value, value, "[no data]"); - else - bus_print_property_valuef(name, expected_value, value, "%"PRIu64, u); - - return 1; - } - - case SD_BUS_TYPE_INT64: { - int64_t i; - - r = sd_bus_message_read_basic(m, type, &i); - if (r < 0) - return r; - - bus_print_property_valuef(name, expected_value, value, "%"PRIi64, i); - return 1; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t u; - - r = sd_bus_message_read_basic(m, type, &u); - if (r < 0) - return r; - - if (strstr(name, "UMask") || strstr(name, "Mode")) - bus_print_property_valuef(name, expected_value, value, "%04o", u); - - else if (streq(name, "UID")) { - if (u == UID_INVALID) - bus_print_property_value(name, expected_value, value, "[not set]"); - else - bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); - } else if (streq(name, "GID")) { - if (u == GID_INVALID) - bus_print_property_value(name, expected_value, value, "[not set]"); - else - bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); - } else - bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); - - return 1; - } - - case SD_BUS_TYPE_INT32: { - int32_t i; - - r = sd_bus_message_read_basic(m, type, &i); - if (r < 0) - return r; - - bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i); - return 1; - } - - case SD_BUS_TYPE_DOUBLE: { - double d; - - r = sd_bus_message_read_basic(m, type, &d); - if (r < 0) - return r; - - bus_print_property_valuef(name, expected_value, value, "%g", d); - return 1; - } - - case SD_BUS_TYPE_ARRAY: - if (streq(contents, "s")) { - bool first = true; - const char *str; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) { - bool good; - - if (first && !value) - printf("%s=", name); - - /* This property has multiple space-separated values, so - * neither spaces nor newlines can be allowed in a value. */ - good = str[strcspn(str, " \n")] == '\0'; - - printf("%s%s", first ? "" : " ", good ? str : "[unprintable]"); - - first = false; - } - if (r < 0) - return r; - - if (first && all && !value) - printf("%s=", name); - if (!first || all) - puts(""); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 1; - - } else if (streq(contents, "y")) { - const uint8_t *u; - size_t n; - - r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n); - if (r < 0) - return r; - - if (all || n > 0) { - unsigned i; - - if (!value) - printf("%s=", name); - - for (i = 0; i < n; i++) - printf("%02x", u[i]); - - puts(""); - } - - return 1; - - } else if (streq(contents, "u")) { - uint32_t *u; - size_t n; - - r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n); - if (r < 0) - return r; - - if (all || n > 0) { - unsigned i; - - if (!value) - printf("%s=", name); - - for (i = 0; i < n; i++) - printf("%08x", u[i]); - - puts(""); - } - - return 1; - } - - break; - } - - return 0; -} - -int bus_message_print_all_properties( - sd_bus_message *m, - bus_message_print_t func, - char **filter, - bool value, - bool all, - Set **found_properties) { - - int r; - - assert(m); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - _cleanup_free_ char *name_with_equal = NULL; - const char *name, *contents, *expected_value = NULL; - - r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name); - if (r < 0) - return r; - - if (found_properties) { - r = set_ensure_allocated(found_properties, &string_hash_ops); - if (r < 0) - return log_oom(); - - r = set_put(*found_properties, name); - if (r < 0 && r != -EEXIST) - return log_oom(); - } - - name_with_equal = strjoin(name, "="); - if (!name_with_equal) - return log_oom(); - - if (!filter || strv_find(filter, name) || - (expected_value = strv_find_startswith(filter, name_with_equal))) { - r = sd_bus_message_peek_type(m, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return r; - - if (func) - r = func(name, expected_value, m, value, all); - if (!func || r == 0) - r = bus_print_property(name, expected_value, m, value, all); - if (r < 0) - return r; - if (r == 0) { - if (all && !expected_value) - printf("%s=[unprintable]\n", name); - /* skip what we didn't read */ - r = sd_bus_message_skip(m, contents); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } else { - r = sd_bus_message_skip(m, "v"); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -int bus_print_all_properties( - sd_bus *bus, - const char *dest, - const char *path, - bus_message_print_t func, - char **filter, - bool value, - bool all, - Set **found_properties) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(path); - - r = sd_bus_call_method(bus, - dest, - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); - if (r < 0) - return r; - - return bus_message_print_all_properties(reply, func, filter, value, all, found_properties); -} - -int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - sd_id128_t *p = userdata; - const void *v; - size_t n; - int r; - - r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n); - if (r < 0) - return r; - - if (n == 0) - *p = SD_ID128_NULL; - else if (n == 16) - memcpy((*p).bytes, v, n); - else - return -EINVAL; - - return 0; -} - -static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) { - char type; - int r; - - r = sd_bus_message_peek_type(m, &type, NULL); - if (r < 0) - return r; - - switch (type) { - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: { - const char **p = userdata; - const char *s; - - r = sd_bus_message_read_basic(m, type, &s); - if (r < 0) - return r; - - if (isempty(s)) - s = NULL; - - if (flags & BUS_MAP_STRDUP) - return free_and_strdup((char **) userdata, s); - - *p = s; - return 0; - } - - case SD_BUS_TYPE_ARRAY: { - _cleanup_strv_free_ char **l = NULL; - char ***p = userdata; - - r = bus_message_read_strv_extend(m, &l); - if (r < 0) - return r; - - return strv_extend_strv(p, l, false); - } - - case SD_BUS_TYPE_BOOLEAN: { - int b; - - r = sd_bus_message_read_basic(m, type, &b); - if (r < 0) - return r; - - if (flags & BUS_MAP_BOOLEAN_AS_BOOL) - *(bool*) userdata = b; - else - *(int*) userdata = b; - - return 0; - } - - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: { - uint32_t u, *p = userdata; - - r = sd_bus_message_read_basic(m, type, &u); - if (r < 0) - return r; - - *p = u; - return 0; - } - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: { - uint64_t t, *p = userdata; - - r = sd_bus_message_read_basic(m, type, &t); - if (r < 0) - return r; - - *p = t; - return 0; - } - - case SD_BUS_TYPE_DOUBLE: { - double d, *p = userdata; - - r = sd_bus_message_read_basic(m, type, &d); - if (r < 0) - return r; - - *p = d; - return 0; - }} - - return -EOPNOTSUPP; -} - -int bus_message_map_all_properties( - sd_bus_message *m, - const struct bus_properties_map *map, - unsigned flags, - sd_bus_error *error, - void *userdata) { - - int r; - - assert(m); - assert(map); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const struct bus_properties_map *prop; - const char *member; - const char *contents; - void *v; - unsigned i; - - r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member); - if (r < 0) - return r; - - for (i = 0, prop = NULL; map[i].member; i++) - if (streq(map[i].member, member)) { - prop = &map[i]; - break; - } - - if (prop) { - r = sd_bus_message_peek_type(m, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return r; - - v = (uint8_t *)userdata + prop->offset; - if (map[i].set) - r = prop->set(sd_bus_message_get_bus(m), member, m, error, v); - else - r = map_basic(sd_bus_message_get_bus(m), member, m, flags, error, v); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } else { - r = sd_bus_message_skip(m, "v"); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - if (r < 0) - return r; - - return sd_bus_message_exit_container(m); -} - -int bus_map_all_properties( - sd_bus *bus, - const char *destination, - const char *path, - const struct bus_properties_map *map, - unsigned flags, - sd_bus_error *error, - sd_bus_message **reply, - void *userdata) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - assert(destination); - assert(path); - assert(map); - assert(reply || (flags & BUS_MAP_STRDUP)); - - r = sd_bus_call_method( - bus, - destination, - path, - "org.freedesktop.DBus.Properties", - "GetAll", - error, - &m, - "s", ""); - if (r < 0) - return r; - - r = bus_message_map_all_properties(m, map, flags, error, userdata); - if (r < 0) - return r; - - if (reply) - *reply = sd_bus_message_ref(m); - - return r; -} - int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) { _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL; int r; @@ -1027,102 +332,6 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool return r; } -int bus_property_get_bool( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - int b = *(bool*) userdata; - - return sd_bus_message_append_basic(reply, 'b', &b); -} - -int bus_property_set_bool( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *error) { - - int b, r; - - r = sd_bus_message_read(value, "b", &b); - if (r < 0) - return r; - - *(bool*) userdata = b; - return 0; -} - -int bus_property_get_id128( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - sd_id128_t *id = userdata; - - if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */ - return sd_bus_message_append(reply, "ay", 0); - else - return sd_bus_message_append_array(reply, 'y', id->bytes, 16); -} - -#if __SIZEOF_SIZE_T__ != 8 -int bus_property_get_size( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t sz = *(size_t*) userdata; - - return sd_bus_message_append_basic(reply, 't', &sz); -} -#endif - -#if __SIZEOF_LONG__ != 8 -int bus_property_get_long( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - int64_t l = *(long*) userdata; - - return sd_bus_message_append_basic(reply, 'x', &l); -} - -int bus_property_get_ulong( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t ul = *(unsigned long*) userdata; - - return sd_bus_message_append_basic(reply, 't', &ul); -} -#endif - /** * bus_path_encode_unique() - encode unique object path * @b: bus connection or NULL @@ -1166,7 +375,7 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, int r; assert_return(b || (sender_id && external_id), -EINVAL); - assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(sd_bus_object_path_is_valid(prefix), -EINVAL); assert_return(ret_path, -EINVAL); if (!sender_id) { @@ -1218,8 +427,8 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send const char *p, *q; char *sender, *external; - assert(object_path_is_valid(path)); - assert(object_path_is_valid(prefix)); + assert(sd_bus_object_path_is_valid(path)); + assert(sd_bus_object_path_is_valid(prefix)); assert(ret_sender); assert(ret_external); @@ -1250,54 +459,6 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send return 1; } -int bus_property_get_rlimit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - const char *is_soft; - struct rlimit *rl; - uint64_t u; - rlim_t x; - - assert(bus); - assert(reply); - assert(userdata); - - is_soft = endswith(property, "Soft"); - - rl = *(struct rlimit**) userdata; - if (rl) - x = is_soft ? rl->rlim_cur : rl->rlim_max; - else { - struct rlimit buf = {}; - const char *s, *p; - int z; - - /* Chop off "Soft" suffix */ - s = is_soft ? strndupa(property, is_soft - property) : property; - - /* Skip over any prefix, such as "Default" */ - assert_se(p = strstr(s, "Limit")); - - z = rlimit_from_string(p + 5); - assert(z >= 0); - - (void) getrlimit(z, &buf); - x = is_soft ? buf.rlim_cur : buf.rlim_max; - } - - /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all - * archs */ - u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; - - return sd_bus_message_append(reply, "t", u); -} - int bus_track_add_name_many(sd_bus_track *t, char **l) { int r = 0; char **i; diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index db245a791..d98e0040f 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -10,7 +10,6 @@ #include "sd-event.h" #include "macro.h" -#include "set.h" #include "string-util.h" #include "time-util.h" @@ -22,26 +21,6 @@ typedef enum BusTransport { _BUS_TRANSPORT_INVALID = -1 } BusTransport; -typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); - -struct bus_properties_map { - const char *member; - const char *signature; - bus_property_set_t set; - size_t offset; -}; - -enum { - BUS_MAP_STRDUP = 1 << 0, /* If set, each "s" message is duplicated. Thus, each pointer needs to be freed. */ - BUS_MAP_BOOLEAN_AS_BOOL = 1 << 1, /* If set, each "b" message is written to a bool pointer. If not set, "b" is written to a int pointer. */ -}; - -int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); - -int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata); -int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, - unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata); - int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name); typedef bool (*check_idle_t)(void *userdata); @@ -58,56 +37,8 @@ int bus_connect_user_systemd(sd_bus **_bus); int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus); int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus); -typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all); - -int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value); -int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5); -int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); -int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); - -int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -int bus_property_set_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error); -int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); - -#define bus_property_get_usec ((sd_bus_property_get_t) NULL) -#define bus_property_set_usec ((sd_bus_property_set_t) NULL) - -assert_cc(sizeof(int) == sizeof(int32_t)); -#define bus_property_get_int ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(unsigned) == sizeof(uint32_t)); -#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL) - -/* On 64bit machines we can use the default serializer for size_t and - * friends, otherwise we need to cast this manually */ -#if __SIZEOF_SIZE_T__ == 8 -#define bus_property_get_size ((sd_bus_property_get_t) NULL) -#else -int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -#endif - -#if __SIZEOF_LONG__ == 8 -#define bus_property_get_long ((sd_bus_property_get_t) NULL) -#define bus_property_get_ulong ((sd_bus_property_get_t) NULL) -#else -int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -#endif - -/* uid_t and friends on Linux 32 bit. This means we can just use the - * default serializer for 32bit unsigned, for serializing it, and map - * it to NULL here */ -assert_cc(sizeof(uid_t) == sizeof(uint32_t)); -#define bus_property_get_uid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(gid_t) == sizeof(uint32_t)); -#define bus_property_get_gid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(pid_t) == sizeof(uint32_t)); -#define bus_property_get_pid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(mode_t) == sizeof(uint32_t)); -#define bus_property_get_mode ((sd_bus_property_get_t) NULL) +#define bus_log_connect_error(r) \ + log_error_errno(r, "Failed to create bus connection: %m") #define bus_log_parse_error(r) \ log_error_errno(r, "Failed to parse bus message: %m") @@ -115,60 +46,9 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t)); #define bus_log_create_error(r) \ log_error_errno(r, "Failed to create bus message: %m") -#define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val) \ - int function(sd_bus *bus, \ - const char *path, \ - const char *interface, \ - const char *property, \ - sd_bus_message *reply, \ - void *userdata, \ - sd_bus_error *error) { \ - \ - assert(bus); \ - assert(reply); \ - \ - return sd_bus_message_append(reply, bus_type, val); \ - } - -#define BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, get2) \ - int function(sd_bus *bus, \ - const char *path, \ - const char *interface, \ - const char *property, \ - sd_bus_message *reply, \ - void *userdata, \ - sd_bus_error *error) { \ - \ - data_type *data = userdata; \ - \ - assert(bus); \ - assert(reply); \ - assert(data); \ - \ - return sd_bus_message_append(reply, bus_type, \ - get2(get1(data))); \ - } - -#define ident(x) (x) -#define BUS_DEFINE_PROPERTY_GET(function, bus_type, data_type, get1) \ - BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, ident) - -#define ref(x) (*(x)) -#define BUS_DEFINE_PROPERTY_GET_REF(function, bus_type, data_type, get) \ - BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, ref, get) - -#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ - BUS_DEFINE_PROPERTY_GET_REF(function, "s", type, name##_to_string) - -#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \ - SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ - SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) - int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); -int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); - int bus_track_add_name_many(sd_bus_track *t, char **l); int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *description); diff --git a/src/shared/bus-wait-for-jobs.c b/src/shared/bus-wait-for-jobs.c index 4e6b862d5..eb33ba234 100644 --- a/src/shared/bus-wait-for-jobs.c +++ b/src/shared/bus-wait-for-jobs.c @@ -65,7 +65,7 @@ void bus_wait_for_jobs_free(BusWaitForJobs *d) { if (!d) return; - set_free_free(d->jobs); + set_free(d->jobs); sd_bus_slot_unref(d->slot_disconnected); sd_bus_slot_unref(d->slot_job_removed); @@ -315,15 +315,9 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar } int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { - int r; - assert(d); - r = set_ensure_allocated(&d->jobs, &string_hash_ops); - if (r < 0) - return r; - - return set_put_strdup(d->jobs, path); + return set_put_strdup(&d->jobs, path); } int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c index 3ee3c0ccb..7592dcf18 100644 --- a/src/shared/bus-wait-for-units.c +++ b/src/shared/bus-wait-for-units.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "bus-util.h" +#include "bus-map-properties.h" #include "bus-wait-for-units.h" #include "hashmap.h" #include "string-util.h" diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 7dd87afd0..db6a103c4 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -17,6 +17,7 @@ #include "process-util.h" #include "sort-util.h" #include "string-util.h" +#include "strv.h" #include "time-util.h" #define BITS_WEEKDAYS 127 @@ -29,6 +30,9 @@ * linked compenents anyway. */ #define CALENDARSPEC_COMPONENTS_MAX 240 +/* Let's make sure that the microsecond component is safe to be stored in an 'int' */ +assert_cc(INT_MAX >= USEC_PER_SEC); + static void chain_free(CalendarComponent *c) { CalendarComponent *n; @@ -171,7 +175,7 @@ int calendar_spec_normalize(CalendarSpec *c) { return 0; } -_pure_ static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) { +static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) { assert(to >= from); if (!c) @@ -980,9 +984,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (r < 0) return r; - } else if (strcaseeq(p, "annually") || - strcaseeq(p, "yearly") || - strcaseeq(p, "anually") /* backwards compatibility */ ) { + } else if (STRCASE_IN_SET(p, + "annually", + "yearly", + "anually") /* backwards compatibility */ ) { r = const_chain(1, &c->month); if (r < 0) @@ -1041,10 +1046,11 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (r < 0) return r; - } else if (strcaseeq(p, "biannually") || - strcaseeq(p, "bi-annually") || - strcaseeq(p, "semiannually") || - strcaseeq(p, "semi-annually")) { + } else if (STRCASE_IN_SET(p, + "biannually", + "bi-annually", + "semiannually", + "semi-annually")) { r = const_chain(1, &c->month); if (r < 0) diff --git a/src/shared/calendarspec.h b/src/shared/calendarspec.h index 3bf8a39e1..0a5d95b4b 100644 --- a/src/shared/calendarspec.h +++ b/src/shared/calendarspec.h @@ -19,9 +19,9 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; - bool end_of_month; - bool utc; - int dst; + bool end_of_month:1; + bool utc:1; + signed int dst:2; char *timezone; CalendarComponent *year; diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 208d27df1..b8bf3c272 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -371,7 +371,7 @@ int show_cgroup_get_path_and_warn( r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); r = show_cgroup_get_unit_path_and_warn(bus, unit, &root); if (r < 0) diff --git a/src/shared/chown-recursive.c b/src/shared/chown-recursive.c index 1aebac307..636c0e2a7 100644 --- a/src/shared/chown-recursive.c +++ b/src/shared/chown-recursive.c @@ -150,7 +150,7 @@ int fd_chown_recursive( struct stat st; /* Note that the slightly different order of fstat() and the checks here and in - * path_chown_recursive(). That's because when we open the dirctory ourselves we can specify + * path_chown_recursive(). That's because when we open the directory ourselves we can specify * O_DIRECTORY and we always want to ensure we are operating on a directory before deciding whether * the operation is otherwise redundant. */ diff --git a/src/shared/condition.c b/src/shared/condition.c index 9f4c7fe33..bf3b5fa16 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -25,6 +25,7 @@ #include "extract-word.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "glob-util.h" #include "hostname-util.h" #include "ima-util.h" @@ -72,11 +73,11 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger return c; } -void condition_free(Condition *c) { +Condition* condition_free(Condition *c) { assert(c); free(c->parameter); - free(c); + return mfree(c); } Condition* condition_free_list_type(Condition *head, ConditionType type) { @@ -92,7 +93,7 @@ Condition* condition_free_list_type(Condition *head, ConditionType type) { return head; } -static int condition_test_kernel_command_line(Condition *c) { +static int condition_test_kernel_command_line(Condition *c, char **env) { _cleanup_free_ char *line = NULL; const char *p; bool equal; @@ -201,7 +202,7 @@ static bool test_order(int k, OrderOperator p) { } } -static int condition_test_kernel_version(Condition *c) { +static int condition_test_kernel_version(Condition *c, char **env) { OrderOperator order; struct utsname u; const char *p; @@ -259,7 +260,7 @@ static int condition_test_kernel_version(Condition *c) { return true; } -static int condition_test_memory(Condition *c) { +static int condition_test_memory(Condition *c, char **env) { OrderOperator order; uint64_t m, k; const char *p; @@ -283,7 +284,7 @@ static int condition_test_memory(Condition *c) { return test_order(CMP(m, k), order); } -static int condition_test_cpus(Condition *c) { +static int condition_test_cpus(Condition *c, char **env) { OrderOperator order; const char *p; unsigned k; @@ -309,7 +310,7 @@ static int condition_test_cpus(Condition *c) { return test_order(CMP((unsigned) n, k), order); } -static int condition_test_user(Condition *c) { +static int condition_test_user(Condition *c, char **env) { uid_t id; int r; _cleanup_free_ char *username = NULL; @@ -344,7 +345,7 @@ static int condition_test_user(Condition *c) { return id == getuid() || id == geteuid(); } -static int condition_test_control_group_controller(Condition *c) { +static int condition_test_control_group_controller(Condition *c, char **env) { int r; CGroupMask system_mask, wanted_mask = 0; @@ -368,7 +369,7 @@ static int condition_test_control_group_controller(Condition *c) { return FLAGS_SET(system_mask, wanted_mask); } -static int condition_test_group(Condition *c) { +static int condition_test_group(Condition *c, char **env) { gid_t id; int r; @@ -387,7 +388,7 @@ static int condition_test_group(Condition *c) { return in_group(c->parameter) > 0; } -static int condition_test_virtualization(Condition *c) { +static int condition_test_virtualization(Condition *c, char **env) { int b, v; assert(c); @@ -417,7 +418,7 @@ static int condition_test_virtualization(Condition *c) { return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v)); } -static int condition_test_architecture(Condition *c) { +static int condition_test_architecture(Condition *c, char **env) { int a, b; assert(c); @@ -439,7 +440,7 @@ static int condition_test_architecture(Condition *c) { return a == b; } -static int condition_test_host(Condition *c) { +static int condition_test_host(Condition *c, char **env) { _cleanup_free_ char *h = NULL; sd_id128_t x, y; int r; @@ -464,7 +465,7 @@ static int condition_test_host(Condition *c) { return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0; } -static int condition_test_ac_power(Condition *c) { +static int condition_test_ac_power(Condition *c, char **env) { int r; assert(c); @@ -478,7 +479,7 @@ static int condition_test_ac_power(Condition *c) { return (on_ac_power() != 0) == !!r; } -static int condition_test_security(Condition *c) { +static int condition_test_security(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_SECURITY); @@ -501,7 +502,7 @@ static int condition_test_security(Condition *c) { return false; } -static int condition_test_capability(Condition *c) { +static int condition_test_capability(Condition *c, char **env) { unsigned long long capabilities = (unsigned long long) -1; _cleanup_fclose_ FILE *f = NULL; int value, r; @@ -544,31 +545,48 @@ static int condition_test_capability(Condition *c) { return !!(capabilities & (1ULL << value)); } -static int condition_test_needs_update(Condition *c) { - const char *p; +static int condition_test_needs_update(Condition *c, char **env) { struct stat usr, other; + const char *p; + bool b; + int r; assert(c); assert(c->parameter); assert(c->type == CONDITION_NEEDS_UPDATE); + r = proc_cmdline_get_bool("systemd.condition-needs-update", &b); + if (r < 0) + log_debug_errno(r, "Failed to parse systemd.condition-needs-update= kernel command line argument, ignoring: %m"); + if (r > 0) + return b; + + if (!path_is_absolute(c->parameter)) { + log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter); + return true; + } + /* If the file system is read-only we shouldn't suggest an update */ - if (path_is_read_only_fs(c->parameter) > 0) + r = path_is_read_only_fs(c->parameter); + if (r < 0) + log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter); + if (r > 0) return false; - /* Any other failure means we should allow the condition to be true, - * so that we rather invoke too many update tools than too - * few. */ - - if (!path_is_absolute(c->parameter)) - return true; + /* Any other failure means we should allow the condition to be true, so that we rather invoke too + * many update tools than too few. */ p = strjoina(c->parameter, "/.updated"); - if (lstat(p, &other) < 0) + if (lstat(p, &other) < 0) { + if (errno != ENOENT) + log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p); return true; + } - if (lstat("/usr/", &usr) < 0) + if (lstat("/usr/", &usr) < 0) { + log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m"); return true; + } /* * First, compare seconds as they are always accurate... @@ -584,47 +602,84 @@ static int condition_test_needs_update(Condition *c) { * AND the target file's nanoseconds == 0 * (otherwise the filesystem supports nsec timestamps, see stat(2)). */ - if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) { - _cleanup_free_ char *timestamp_str = NULL; - uint64_t timestamp; - int r; + if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0) + return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec; - r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", ×tamp_str); - if (r < 0) { - log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); - return true; - } else if (r == 0) { - log_debug("No data in timestamp file '%s', using mtime", p); - return true; - } - - r = safe_atou64(timestamp_str, ×tamp); - if (r < 0) { - log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p); - return true; - } - - timespec_store(&other.st_mtim, timestamp); + _cleanup_free_ char *timestamp_str = NULL; + r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", ×tamp_str); + if (r < 0) { + log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); + return true; + } else if (r == 0) { + log_debug("No data in timestamp file '%s', using mtime.", p); + return true; } - return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec; + uint64_t timestamp; + r = safe_atou64(timestamp_str, ×tamp); + if (r < 0) { + log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p); + return true; + } + + return timespec_load_nsec(&usr.st_mtim) > timestamp; } -static int condition_test_first_boot(Condition *c) { - int r; +static int condition_test_first_boot(Condition *c, char **env) { + int r, q; + bool b; assert(c); assert(c->parameter); assert(c->type == CONDITION_FIRST_BOOT); + r = proc_cmdline_get_bool("systemd.condition-first-boot", &b); + if (r < 0) + log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel command line argument, ignoring: %m"); + if (r > 0) + return b == !!r; + r = parse_boolean(c->parameter); if (r < 0) return r; - return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r; + q = access("/run/systemd/first-boot", F_OK); + if (q < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, ignoring: %m"); + + return (q >= 0) == !!r; } -static int condition_test_path_exists(Condition *c) { +static int condition_test_environment(Condition *c, char **env) { + bool equal; + char **i; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_ENVIRONMENT); + + equal = strchr(c->parameter, '='); + + STRV_FOREACH(i, env) { + bool found; + + if (equal) + found = streq(c->parameter, *i); + else { + const char *f; + + f = startswith(*i, c->parameter); + found = f && IN_SET(*f, 0, '='); + } + + if (found) + return true; + } + + return false; +} + +static int condition_test_path_exists(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_EXISTS); @@ -632,7 +687,7 @@ static int condition_test_path_exists(Condition *c) { return access(c->parameter, F_OK) >= 0; } -static int condition_test_path_exists_glob(Condition *c) { +static int condition_test_path_exists_glob(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_EXISTS_GLOB); @@ -640,7 +695,7 @@ static int condition_test_path_exists_glob(Condition *c) { return glob_exists(c->parameter) > 0; } -static int condition_test_path_is_directory(Condition *c) { +static int condition_test_path_is_directory(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_IS_DIRECTORY); @@ -648,7 +703,7 @@ static int condition_test_path_is_directory(Condition *c) { return is_dir(c->parameter, true) > 0; } -static int condition_test_path_is_symbolic_link(Condition *c) { +static int condition_test_path_is_symbolic_link(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK); @@ -656,7 +711,7 @@ static int condition_test_path_is_symbolic_link(Condition *c) { return is_symlink(c->parameter) > 0; } -static int condition_test_path_is_mount_point(Condition *c) { +static int condition_test_path_is_mount_point(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); @@ -664,7 +719,7 @@ static int condition_test_path_is_mount_point(Condition *c) { return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0; } -static int condition_test_path_is_read_write(Condition *c) { +static int condition_test_path_is_read_write(Condition *c, char **env) { assert(c); assert(c->parameter); assert(c->type == CONDITION_PATH_IS_READ_WRITE); @@ -672,7 +727,21 @@ static int condition_test_path_is_read_write(Condition *c) { return path_is_read_only_fs(c->parameter) <= 0; } -static int condition_test_directory_not_empty(Condition *c) { +static int condition_test_path_is_encrypted(Condition *c, char **env) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_ENCRYPTED); + + r = path_is_encrypted(c->parameter); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to determine if '%s' is encrypted: %m", c->parameter); + + return r > 0; +} + +static int condition_test_directory_not_empty(Condition *c, char **env) { int r; assert(c); @@ -683,7 +752,7 @@ static int condition_test_directory_not_empty(Condition *c) { return r <= 0 && r != -ENOENT; } -static int condition_test_file_not_empty(Condition *c) { +static int condition_test_file_not_empty(Condition *c, char **env) { struct stat st; assert(c); @@ -695,7 +764,7 @@ static int condition_test_file_not_empty(Condition *c) { st.st_size > 0); } -static int condition_test_file_is_executable(Condition *c) { +static int condition_test_file_is_executable(Condition *c, char **env) { struct stat st; assert(c); @@ -707,7 +776,7 @@ static int condition_test_file_is_executable(Condition *c) { (st.st_mode & 0111)); } -static int condition_test_null(Condition *c) { +static int condition_test_null(Condition *c, char **env) { assert(c); assert(c->type == CONDITION_NULL); @@ -716,15 +785,16 @@ static int condition_test_null(Condition *c) { return true; } -int condition_test(Condition *c) { +int condition_test(Condition *c, char **env) { - static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = { + static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = { [CONDITION_PATH_EXISTS] = condition_test_path_exists, [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob, [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory, [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link, [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point, [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write, + [CONDITION_PATH_IS_ENCRYPTED] = condition_test_path_is_encrypted, [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty, [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, @@ -744,6 +814,7 @@ int condition_test(Condition *c) { [CONDITION_NULL] = condition_test_null, [CONDITION_CPUS] = condition_test_cpus, [CONDITION_MEMORY] = condition_test_memory, + [CONDITION_ENVIRONMENT] = condition_test_environment, }; int r, b; @@ -752,7 +823,7 @@ int condition_test(Condition *c) { assert(c->type >= 0); assert(c->type < _CONDITION_TYPE_MAX); - r = condition_tests[c->type](c); + r = condition_tests[c->type](c, env); if (r < 0) { c->result = CONDITION_ERROR; return r; @@ -763,7 +834,13 @@ int condition_test(Condition *c) { return b; } -bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata) { +bool condition_test_list( + Condition *first, + char **env, + condition_to_string_t to_string, + condition_test_logger_t logger, + void *userdata) { + Condition *c; int triggered = -1; @@ -779,7 +856,7 @@ bool condition_test_list(Condition *first, const char *(*to_string)(ConditionTyp LIST_FOREACH(conditions, c, first) { int r; - r = condition_test(c); + r = condition_test(c, env); if (logger) { const char *p = c->type == CONDITION_NULL ? "true" : c->parameter; @@ -812,9 +889,10 @@ bool condition_test_list(Condition *first, const char *(*to_string)(ConditionTyp return triggered != 0; } -void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { +void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) { assert(c); assert(f); + assert(to_string); prefix = strempty(prefix); @@ -828,7 +906,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_ condition_result_to_string(c->result)); } -void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { +void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) { Condition *c; LIST_FOREACH(conditions, c, first) @@ -852,6 +930,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", + [CONDITION_PATH_IS_ENCRYPTED] = "ConditionPathIsEncrypted", [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", @@ -861,6 +940,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_NULL] = "ConditionNull", [CONDITION_CPUS] = "ConditionCPUs", [CONDITION_MEMORY] = "ConditionMemory", + [CONDITION_ENVIRONMENT] = "ConditionEnvironment", }; DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); @@ -882,6 +962,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink", [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint", [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite", + [CONDITION_PATH_IS_ENCRYPTED] = "AssertPathIsEncrypted", [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", @@ -891,6 +972,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_NULL] = "AssertNull", [CONDITION_CPUS] = "AssertCPUs", [CONDITION_MEMORY] = "AssertMemory", + [CONDITION_ENVIRONMENT] = "AssertEnvironment", }; DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); diff --git a/src/shared/condition.h b/src/shared/condition.h index 84322e742..fea74d228 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -18,6 +18,7 @@ typedef enum ConditionType { CONDITION_AC_POWER, CONDITION_MEMORY, CONDITION_CPUS, + CONDITION_ENVIRONMENT, CONDITION_NEEDS_UPDATE, CONDITION_FIRST_BOOT, @@ -28,6 +29,7 @@ typedef enum ConditionType { CONDITION_PATH_IS_SYMBOLIC_LINK, CONDITION_PATH_IS_MOUNT_POINT, CONDITION_PATH_IS_READ_WRITE, + CONDITION_PATH_IS_ENCRYPTED, CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_FILE_NOT_EMPTY, CONDITION_FILE_IS_EXECUTABLE, @@ -66,18 +68,20 @@ typedef struct Condition { } Condition; Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate); -void condition_free(Condition *c); +Condition* condition_free(Condition *c); Condition* condition_free_list_type(Condition *first, ConditionType type); static inline Condition* condition_free_list(Condition *first) { return condition_free_list_type(first, _CONDITION_TYPE_INVALID); } -int condition_test(Condition *c); -typedef int (*condition_test_logger_t)(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8); -bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata); +int condition_test(Condition *c, char **env); -void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); -void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); +typedef int (*condition_test_logger_t)(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8); +typedef const char* (*condition_to_string_t)(ConditionType t) _const_; +bool condition_test_list(Condition *first, char **env, condition_to_string_t to_string, condition_test_logger_t logger, void *userdata); + +void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string); +void condition_dump_list(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string); const char* condition_type_to_string(ConditionType t) _const_; ConditionType condition_type_from_string(const char *s) _pure_; @@ -96,6 +100,7 @@ static inline bool condition_takes_path(ConditionType t) { CONDITION_PATH_IS_SYMBOLIC_LINK, CONDITION_PATH_IS_MOUNT_POINT, CONDITION_PATH_IS_READ_WRITE, + CONDITION_PATH_IS_ENCRYPTED, CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_FILE_NOT_EMPTY, CONDITION_FILE_IS_EXECUTABLE, diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 811211ccf..0fec79f3d 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -23,6 +23,7 @@ #include "path-util.h" #include "process-util.h" #include "rlimit-util.h" +#include "sd-id128.h" #include "signal-util.h" #include "socket-util.h" #include "string-util.h" @@ -158,7 +159,7 @@ static int parse_line( char *l, void *userdata) { - char *e, *include; + char *e; assert(filename); assert(line > 0); @@ -172,35 +173,6 @@ static int parse_line( if (*l == '\n') return 0; - include = first_word(l, ".include"); - if (include) { - _cleanup_free_ char *fn = NULL; - - /* .includes are a bad idea, we only support them here - * for historical reasons. They create cyclic include - * problems and make it difficult to detect - * configuration file changes with an easy - * stat(). Better approaches, such as .d/ drop-in - * snippets exist. - * - * Support for them should be eventually removed. */ - - if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); - return 0; - } - - log_syntax(unit, LOG_WARNING, filename, line, 0, - ".include directives are deprecated, and support for them will be removed in a future version of systemd. " - "Please use drop-in files instead."); - - fn = file_in_same_dir(filename, strstrip(include)); - if (!fn) - return -ENOMEM; - - return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata); - } - if (!utf8_is_valid(l)) return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); @@ -288,13 +260,15 @@ int config_parse(const char *unit, ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata) { + void *userdata, + usec_t *ret_mtime) { _cleanup_free_ char *section = NULL, *continuation = NULL; _cleanup_fclose_ FILE *ours = NULL; unsigned line = 0, section_line = 0; bool section_ignored = false, bom_seen = false; int r, fd; + usec_t mtime; assert(filename); assert(lookup); @@ -312,8 +286,16 @@ int config_parse(const char *unit, } fd = fileno(f); - if (fd >= 0) /* stream might not have an fd, let's be careful hence */ - fd_warn_permissions(filename, fd); + if (fd >= 0) { /* stream might not have an fd, let's be careful hence */ + struct stat st; + + if (fstat(fd, &st) < 0) + return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno, + "Failed to fstat(%s): %m", filename); + + (void) stat_warn_permissions(filename, &st); + mtime = timespec_load(&st.st_mtim); + } for (;;) { _cleanup_free_ char *buf = NULL; @@ -433,6 +415,9 @@ int config_parse(const char *unit, } } + if (ret_mtime) + *ret_mtime = mtime; + return 0; } @@ -443,23 +428,32 @@ static int config_parse_many_files( ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata) { + void *userdata, + usec_t *ret_mtime) { + usec_t mtime = 0; char **fn; int r; if (conf_file) { - r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata); + r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata, &mtime); if (r < 0) return r; } STRV_FOREACH(fn, files) { - r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata); + usec_t t; + + r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t); if (r < 0) return r; + if (t > mtime) /* Find the newest */ + mtime = t; } + if (ret_mtime) + *ret_mtime = mtime; + return 0; } @@ -471,7 +465,8 @@ int config_parse_many_nulstr( ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata) { + void *userdata, + usec_t *ret_mtime) { _cleanup_strv_free_ char **files = NULL; int r; @@ -480,7 +475,7 @@ int config_parse_many_nulstr( if (r < 0) return r; - return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); + return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime); } /* Parse each config file in the directories specified as strv. */ @@ -493,7 +488,7 @@ int config_parse_many( const void *table, ConfigParseFlags flags, void *userdata, - char ***ret_dropins) { + usec_t *ret_mtime) { _cleanup_strv_free_ char **dropin_dirs = NULL; _cleanup_strv_free_ char **files = NULL; @@ -509,14 +504,7 @@ int config_parse_many( if (r < 0) return r; - r = config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); - if (r < 0) - return r; - - if (ret_dropins) - *ret_dropins = TAKE_PTR(files); - - return 0; + return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime); } #define DEFINE_PARSER(type, vartype, conv_func) \ @@ -560,7 +548,7 @@ int config_parse_iec_size(const char* unit, if (r >= 0 && (uint64_t) (size_t) v != v) r = -ERANGE; if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); return 0; } @@ -589,10 +577,8 @@ int config_parse_si_uint64( assert(data); r = parse_size(rvalue, 1000, sz); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); - return 0; - } + if (r < 0) + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); return 0; } @@ -619,7 +605,7 @@ int config_parse_iec_uint64( r = parse_size(rvalue, 1024, bytes); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); return 0; } @@ -646,7 +632,7 @@ int config_parse_bool(const char* unit, k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, + log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, k, "Failed to parse boolean value%s: %s", fatal ? "" : ", ignoring", rvalue); return fatal ? -ENOEXEC : 0; @@ -656,6 +642,40 @@ int config_parse_bool(const char* unit, return 0; } +int config_parse_id128( + 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) { + + sd_id128_t t, *result = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = sd_id128_from_string(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); + return 0; + } + + if (sd_id128_is_null(t)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "128bit ID/UUID is all 0, ignoring: %s", rvalue); + return 0; + } + + *result = t; + return 0; +} + int config_parse_tristate( const char* unit, const char *filename, @@ -681,7 +701,7 @@ int config_parse_tristate( k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); return 0; } @@ -776,25 +796,23 @@ int config_parse_strv( return 0; } - for (;;) { + for (const char *p = rvalue;;) { char *word = NULL; - r = extract_first_word(&rvalue, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); if (r == 0) - break; + return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - break; + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; } r = strv_consume(sv, word); if (r < 0) return log_oom(); } - - return 0; } int config_parse_warn_compat( @@ -853,7 +871,7 @@ int config_parse_log_facility( x = log_facility_unshifted_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue); return 0; } @@ -883,7 +901,7 @@ int config_parse_log_level( x = log_level_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue); return 0; } @@ -916,7 +934,7 @@ int config_parse_signal( r = signal_from_string(rvalue); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue); return 0; } @@ -948,7 +966,7 @@ int config_parse_personality( else { p = personality_from_string(rvalue); if (p == PERSONALITY_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); return 0; } } @@ -983,7 +1001,7 @@ int config_parse_ifname( } if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); return 0; } @@ -1008,7 +1026,6 @@ int config_parse_ifnames( _cleanup_strv_free_ char **names = NULL; char ***s = data; - const char *p; int r; assert(filename); @@ -1021,13 +1038,14 @@ int config_parse_ifnames( return 0; } - p = rvalue; - for (;;) { + for (const char *p = rvalue;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract interface name, ignoring assignment: %s", rvalue); return 0; @@ -1036,7 +1054,7 @@ int config_parse_ifnames( break; if (!ifname_valid_full(word, ltype)) { - log_syntax(unit, LOG_ERR, filename, line, 0, + log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", word); continue; @@ -1082,7 +1100,7 @@ int config_parse_ip_port( r = parse_ip_port(rvalue, &port); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse port '%s'.", rvalue); return 0; } @@ -1111,14 +1129,14 @@ int config_parse_mtu( r = parse_mtu(ltype, rvalue, mtu); if (r == -ERANGE) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s", (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX, rvalue); return 0; } if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse MTU value '%s', ignoring: %m", rvalue); return 0; } @@ -1150,7 +1168,7 @@ int config_parse_rlimit( return 0; } if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); return 0; } @@ -1186,7 +1204,7 @@ int config_parse_permille(const char* unit, r = parse_permille(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse permille value, ignoring: %s", rvalue); return 0; } @@ -1195,3 +1213,35 @@ int config_parse_permille(const char* unit, return 0; } + +int config_parse_vlanprotocol(const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + int *vlan_protocol = data; + assert(filename); + assert(lvalue); + + if (isempty(rvalue)) { + *vlan_protocol = -1; + return 0; + } + + if (STR_IN_SET(rvalue, "802.1ad", "802.1AD")) + *vlan_protocol = ETH_P_8021AD; + else if (STR_IN_SET(rvalue, "802.1q", "802.1Q")) + *vlan_protocol = ETH_P_8021Q; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse VLAN protocol value, ignoring: %s", rvalue); + return 0; + } + + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 955c8b696..7c9f5531b 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -10,13 +10,13 @@ #include "alloc-util.h" #include "log.h" #include "macro.h" +#include "time-util.h" /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */ typedef enum ConfigParseFlags { CONFIG_PARSE_RELAXED = 1 << 0, /* Do not warn about unknown non-extension fields */ - CONFIG_PARSE_ALLOW_INCLUDE = 1 << 1, /* Allow the deprecated .include stanza */ - CONFIG_PARSE_WARN = 1 << 2, /* Emit non-debug messages */ + CONFIG_PARSE_WARN = 1 << 1, /* Emit non-debug messages */ } ConfigParseFlags; /* Argument list for parsers of specific configuration settings. */ @@ -84,11 +84,12 @@ int config_parse( const char *unit, const char *filename, FILE *f, - const char *sections, /* nulstr */ + const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata); + void *userdata, + usec_t *ret_mtime); /* possibly NULL */ int config_parse_many_nulstr( const char *conf_file, /* possibly NULL */ @@ -97,7 +98,8 @@ int config_parse_many_nulstr( ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata); + void *userdata, + usec_t *ret_mtime); /* possibly NULL */ int config_parse_many( const char *conf_file, /* possibly NULL */ @@ -108,7 +110,7 @@ int config_parse_many( const void *table, ConfigParseFlags flags, void *userdata, - char ***ret_dropins); /* possibly NULL */ + usec_t *ret_mtime); /* possibly NULL */ CONFIG_PARSER_PROTOTYPE(config_parse_int); CONFIG_PARSER_PROTOTYPE(config_parse_unsigned); @@ -123,6 +125,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_iec_size); CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64); CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64); 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_path); @@ -143,6 +146,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); CONFIG_PARSER_PROTOTYPE(config_parse_ip_port); CONFIG_PARSER_PROTOTYPE(config_parse_mtu); CONFIG_PARSER_PROTOTYPE(config_parse_rlimit); +CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol); typedef enum Disabled { DISABLED_CONFIGURATION, @@ -161,7 +165,7 @@ typedef enum Disabled { \ r = parser(rvalue); \ if (r < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, r, \ + log_syntax(unit, LOG_WARNING, filename, line, r, \ msg ", ignoring: %s", rvalue); \ return 0; \ } \ @@ -182,7 +186,7 @@ typedef enum Disabled { \ r = parser(rvalue, i); \ if (r < 0) \ - log_syntax(unit, LOG_ERR, filename, line, r, \ + log_syntax(unit, LOG_WARNING, filename, line, r, \ msg ", ignoring: %s", rvalue); \ \ return 0; \ @@ -199,7 +203,7 @@ typedef enum Disabled { \ x = name##_from_string(rvalue); \ if (x < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, 0, \ + log_syntax(unit, LOG_WARNING, filename, line, 0, \ msg ", ignoring: %s", rvalue); \ return 0; \ } \ @@ -224,7 +228,7 @@ typedef enum Disabled { \ x = name##_from_string(rvalue); \ if (x < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, 0, \ + log_syntax(unit, LOG_WARNING, filename, line, 0, \ msg ", ignoring: %s", rvalue); \ return 0; \ } \ @@ -257,10 +261,10 @@ typedef enum Disabled { \ en = strndup(word, l); \ if (!en) \ - return -ENOMEM; \ + return log_oom(); \ \ if ((x = name##_from_string(en)) < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, 0, \ + log_syntax(unit, LOG_WARNING, filename, line, 0, \ msg ", ignoring: %s", en); \ continue; \ } \ @@ -283,7 +287,7 @@ typedef enum Disabled { if (new_xs) \ xs = new_xs; \ else \ - return -ENOMEM; \ + return log_oom(); \ \ *(xs + i) = invalid; \ } \ diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c new file mode 100644 index 000000000..67897414b --- /dev/null +++ b/src/shared/coredump-util.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "coredump-util.h" +#include "extract-word.h" +#include "fileio.h" +#include "string-table.h" + +static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = { + [COREDUMP_FILTER_PRIVATE_ANONYMOUS] = "private-anonymous", + [COREDUMP_FILTER_SHARED_ANONYMOUS] = "shared-anonymous", + [COREDUMP_FILTER_PRIVATE_FILE_BACKED] = "private-file-backed", + [COREDUMP_FILTER_SHARED_FILE_BACKED] = "shared-file-backed", + [COREDUMP_FILTER_ELF_HEADERS] = "elf-headers", + [COREDUMP_FILTER_PRIVATE_HUGE] = "private-huge", + [COREDUMP_FILTER_SHARED_HUGE] = "shared-huge", + [COREDUMP_FILTER_PRIVATE_DAX] = "private-dax", + [COREDUMP_FILTER_SHARED_DAX] = "shared-dax", +}; + +DEFINE_STRING_TABLE_LOOKUP(coredump_filter, CoredumpFilter); + +int coredump_filter_mask_from_string(const char *s, uint64_t *ret) { + uint64_t m = 0; + + assert(s); + assert(ret); + + for (;;) { + _cleanup_free_ char *n = NULL; + CoredumpFilter v; + int r; + + r = extract_first_word(&s, &n, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + if (streq(n, "default")) { + m |= COREDUMP_FILTER_MASK_DEFAULT; + continue; + } + + if (streq(n, "all")) { + m = UINT64_MAX; + continue; + } + + v = coredump_filter_from_string(n); + if (v >= 0) { + m |= 1u << v; + continue; + } + + uint64_t x; + r = safe_atoux64(n, &x); + if (r < 0) + return r; + + m |= x; + } + + *ret = m; + return 0; +} + +int set_coredump_filter(uint64_t value) { + char t[STRLEN("0xFFFFFFFF")]; + + sprintf(t, "0x%"PRIx64, value); + + return write_string_file("/proc/self/coredump_filter", t, + WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); +} diff --git a/src/shared/coredump-util.h b/src/shared/coredump-util.h new file mode 100644 index 000000000..ff2e511bf --- /dev/null +++ b/src/shared/coredump-util.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "macro.h" + +typedef enum CoredumpFilter { + COREDUMP_FILTER_PRIVATE_ANONYMOUS = 0, + COREDUMP_FILTER_SHARED_ANONYMOUS, + COREDUMP_FILTER_PRIVATE_FILE_BACKED, + COREDUMP_FILTER_SHARED_FILE_BACKED, + COREDUMP_FILTER_ELF_HEADERS, + COREDUMP_FILTER_PRIVATE_HUGE, + COREDUMP_FILTER_SHARED_HUGE, + COREDUMP_FILTER_PRIVATE_DAX, + COREDUMP_FILTER_SHARED_DAX, + _COREDUMP_FILTER_MAX, + _COREDUMP_FILTER_INVALID = -1, +} CoredumpFilter; + +#define COREDUMP_FILTER_MASK_DEFAULT (1u << COREDUMP_FILTER_PRIVATE_ANONYMOUS | \ + 1u << COREDUMP_FILTER_SHARED_ANONYMOUS | \ + 1u << COREDUMP_FILTER_ELF_HEADERS | \ + 1u << COREDUMP_FILTER_PRIVATE_HUGE) + +const char* coredump_filter_to_string(CoredumpFilter i) _const_; +CoredumpFilter coredump_filter_from_string(const char *s) _pure_; +int coredump_filter_mask_from_string(const char *s, uint64_t *ret); + +int set_coredump_filter(uint64_t value); diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c index 4bce8b167..6e57e2a99 100644 --- a/src/shared/dev-setup.c +++ b/src/shared/dev-setup.c @@ -56,31 +56,38 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) { return 0; } -int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) { +int make_inaccessible_nodes( + const char *runtime_dir, + uid_t uid, + gid_t gid) { + static const struct { const char *name; mode_t mode; } table[] = { - { "", S_IFDIR | 0755 }, - { "/inaccessible", S_IFDIR | 0000 }, - { "/inaccessible/reg", S_IFREG | 0000 }, - { "/inaccessible/dir", S_IFDIR | 0000 }, - { "/inaccessible/fifo", S_IFIFO | 0000 }, - { "/inaccessible/sock", S_IFSOCK | 0000 }, + { "/systemd", S_IFDIR | 0755 }, + { "/systemd/inaccessible", S_IFDIR | 0000 }, + { "/systemd/inaccessible/reg", S_IFREG | 0000 }, + { "/systemd/inaccessible/dir", S_IFDIR | 0000 }, + { "/systemd/inaccessible/fifo", S_IFIFO | 0000 }, + { "/systemd/inaccessible/sock", S_IFSOCK | 0000 }, /* The following two are likely to fail if we lack the privs for it (for example in an userns * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0 * device nodes to be created). But that's entirely fine. Consumers of these files should carry * fallback to use a different node then, for example /inaccessible/sock, which is close * enough in behaviour and semantics for most uses. */ - { "/inaccessible/chr", S_IFCHR | 0000 }, - { "/inaccessible/blk", S_IFBLK | 0000 }, + { "/systemd/inaccessible/chr", S_IFCHR | 0000 }, + { "/systemd/inaccessible/blk", S_IFBLK | 0000 }, }; _cleanup_umask_ mode_t u; size_t i; int r; + if (!runtime_dir) + runtime_dir = "/run"; + u = umask(0000); /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting @@ -91,17 +98,17 @@ int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) { for (i = 0; i < ELEMENTSOF(table); i++) { _cleanup_free_ char *path = NULL; - path = path_join(root, table[i].name); + path = path_join(runtime_dir, table[i].name); if (!path) return log_oom(); if (S_ISDIR(table[i].mode)) - r = mkdir(path, table[i].mode & 07777); + r = mkdir_label(path, table[i].mode & 07777); else - r = mknod(path, table[i].mode, makedev(0, 0)); + r = mknod_label(path, table[i].mode, makedev(0, 0)); if (r < 0) { - if (errno != EEXIST) - log_debug_errno(errno, "Failed to create '%s', ignoring: %m", path); + if (r != -EEXIST) + log_debug_errno(r, "Failed to create '%s', ignoring: %m", path); continue; } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 7142b0703..24be6de6c 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -75,10 +75,9 @@ int probe_filesystem(const char *node, char **ret_fstype) { log_debug("No type detected on partition %s", node); goto not_found; } - if (r == -2) { - log_debug("Results ambiguous for partition %s", node); - return -EUCLEAN; - } + if (r == -2) + return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), + "Results ambiguous for partition %s", node); if (r != 0) return errno_or_else(EIO); @@ -308,6 +307,7 @@ int dissect_image( int fd, const void *root_hash, size_t root_hash_size, + const char *verity_data, DissectImageFlags flags, DissectedImage **ret) { @@ -330,6 +330,7 @@ int dissect_image( assert(fd >= 0); assert(ret); assert(root_hash || root_hash_size == 0); + assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE))); /* Probes a disk image, and returns information about what it found in *ret. * @@ -392,8 +393,9 @@ int dissect_image( if (r < 0) return r; - if (!(flags & DISSECT_IMAGE_GPT_ONLY) && - (flags & DISSECT_IMAGE_REQUIRE_ROOT)) { + if ((!(flags & DISSECT_IMAGE_GPT_ONLY) && + (flags & DISSECT_IMAGE_REQUIRE_ROOT)) || + (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) { const char *usage = NULL; (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL); @@ -414,9 +416,13 @@ int dissect_image( if (r < 0) return r; + m->single_file_system = true; + m->verity = root_hash && verity_data; + m->can_verity = !!verity_data; + m->partitions[PARTITION_ROOT] = (DissectedPartition) { .found = true, - .rw = true, + .rw = !m->verity, .partno = -1, .architecture = _ARCHITECTURE_INVALID, .fstype = TAKE_PTR(t), @@ -1134,8 +1140,9 @@ static int make_dm_name_and_node(const void *original_node, const char *suffix, base = strrchr(original_node, '/'); if (!base) - return -EINVAL; - base++; + base = original_node; + else + base++; if (isempty(base)) return -EINVAL; @@ -1211,40 +1218,102 @@ static int decrypt_partition( return 0; } +static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool has_sig, const char *name, struct crypt_device **ret_cd) { + /* If the same volume was already open, check that the root hashes match, and reuse it if they do */ + _cleanup_free_ char *root_hash_existing = NULL; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + struct crypt_params_verity crypt_params = {}; + size_t root_hash_existing_size = root_hash_size; + int r; + + assert(ret_cd); + + r = crypt_init_by_name(&cd, name); + if (r < 0) + return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m"); + r = crypt_get_verity_info(cd, &crypt_params); + if (r < 0) + return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m"); + root_hash_existing = malloc0(root_hash_size); + if (!root_hash_existing) + return -ENOMEM; + r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0); + if (r < 0) + return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m"); + if (root_hash_size != root_hash_existing_size || memcmp(root_hash_existing, root_hash, root_hash_size) != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different."); +#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY + /* Ensure that, if signatures are supported, we only reuse the device if the previous mount + * used the same settings, so that a previous unsigned mount will not be reused if the user + * asks to use signing for the new one, and viceversa. */ + if (has_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same."); +#endif + + *ret_cd = TAKE_PTR(cd); + return 0; +} + +static inline void dm_deferred_remove_clean(char *name) { + if (!name) + return; + (void) crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED); + free(name); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean); + static int verity_partition( DissectedPartition *m, DissectedPartition *v, const void *root_hash, size_t root_hash_size, + const char *verity_data, + const char *root_hash_sig_path, + const void *root_hash_sig, + size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage *d) { - _cleanup_free_ char *node = NULL, *name = NULL; + _cleanup_free_ char *node = NULL, *name = NULL, *hash_sig_from_file = NULL; _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL; int r; assert(m); - assert(v); + assert(v || verity_data); if (!root_hash) return 0; if (!m->found || !m->node || !m->fstype) return 0; - if (!v->found || !v->node || !v->fstype) - return 0; + if (!verity_data) { + if (!v->found || !v->node || !v->fstype) + return 0; - if (!streq(v->fstype, "DM_verity_hash")) - return 0; + if (!streq(v->fstype, "DM_verity_hash")) + return 0; + } - r = make_dm_name_and_node(m->node, "-verity", &name, &node); + if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) { + /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */ + _cleanup_free_ char *root_hash_encoded = NULL; + root_hash_encoded = hexmem(root_hash, root_hash_size); + if (!root_hash_encoded) + return -ENOMEM; + r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node); + } else + r = make_dm_name_and_node(m->node, "-verity", &name, &node); if (r < 0) return r; - if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1)) - return -ENOMEM; + if (!root_hash_sig && root_hash_sig_path) { + r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, &hash_sig_from_file, &root_hash_sig_size); + if (r < 0) + return r; + } - r = crypt_init(&cd, v->node); + r = crypt_init(&cd, verity_data ?: v->node); if (r < 0) return r; @@ -1258,9 +1327,69 @@ static int verity_partition( if (r < 0) return r; - r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); - if (r < 0) - return r; + if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1)) + return -ENOMEM; + + /* If activating fails because the device already exists, check the metadata and reuse it if it matches. + * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time, + * retry a few times before giving up. */ + for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) { + if (root_hash_sig || hash_sig_from_file) { +#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY + r = crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, CRYPT_ACTIVATE_READONLY); +#else + r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()"); +#endif + } else + r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); + /* libdevmapper can return EINVAL when the device is already in the activation stage. + * There's no way to distinguish this situation from a genuine error due to invalid + * parameters, so immediately fallback to activating the device with a unique name. + * Improvements in libcrypsetup can ensure this never happens: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */ + if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) + return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); + if (!IN_SET(r, 0, -EEXIST, -ENODEV)) + return r; + if (r == -EEXIST) { + struct crypt_device *existing_cd = NULL; + + if (!restore_deferred_remove){ + /* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */ + r = dm_deferred_remove_cancel(name); + if (r < 0) + return log_debug_errno(r, "Disabling automated deferred removal for verity device %s failed: %m", node); + restore_deferred_remove = strdup(name); + if (!restore_deferred_remove) + return -ENOMEM; + } + + r = verity_can_reuse(root_hash, root_hash_size, !!root_hash_sig || !!hash_sig_from_file, name, &existing_cd); + /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */ + if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) + return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); + if (!IN_SET(r, 0, -ENODEV, -ENOENT)) + return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node); + if (r == 0) { + if (cd) + crypt_free(cd); + cd = existing_cd; + } + } + if (r == 0) + break; + } + + /* Sanity check: libdevmapper is known to report that the device already exists and is active, + * but it's actually not there, so the later filesystem probe or mount would fail. */ + if (r == 0) + r = access(node, F_OK); + /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time. + * Fall back to activating it with a unique device name. */ + if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) + return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d); + + /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */ + restore_deferred_remove = mfree(restore_deferred_remove); d->decrypted[d->n_decrypted].name = TAKE_PTR(name); d->decrypted[d->n_decrypted].device = TAKE_PTR(cd); @@ -1277,6 +1406,10 @@ int dissected_image_decrypt( const char *passphrase, const void *root_hash, size_t root_hash_size, + const char *verity_data, + const char *root_hash_sig_path, + const void *root_hash_sig, + size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret) { @@ -1323,7 +1456,7 @@ int dissected_image_decrypt( k = PARTITION_VERITY_OF(i); if (k >= 0) { - r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d); + r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags | DISSECT_IMAGE_VERITY_SHARE, d); if (r < 0) return r; } @@ -1348,6 +1481,10 @@ int dissected_image_decrypt_interactively( const char *passphrase, const void *root_hash, size_t root_hash_size, + const char *verity_data, + const char *root_hash_sig_path, + const void *root_hash_sig, + size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret) { @@ -1358,7 +1495,7 @@ int dissected_image_decrypt_interactively( n--; for (;;) { - r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret); + r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags, ret); if (r >= 0) return r; if (r == -EKEYREJECTED) @@ -1399,7 +1536,7 @@ int decrypted_image_relinquish(DecryptedImage *d) { if (p->relinquished) continue; - r = dm_deferred_remove(p->name); + r = crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED); if (r < 0) return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name); @@ -1410,56 +1547,119 @@ int decrypted_image_relinquish(DecryptedImage *d) { return 0; } -int root_hash_load(const char *image, void **ret, size_t *ret_size) { - _cleanup_free_ char *text = NULL; - _cleanup_free_ void *k = NULL; - size_t l; +int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig) { + _cleanup_free_ char *verity_filename = NULL, *roothashsig_filename = NULL; + _cleanup_free_ void *roothash_decoded = NULL; + size_t roothash_decoded_size = 0; int r; assert(image); - assert(ret); - assert(ret_size); if (is_device_path(image)) { /* If we are asked to load the root hash for a device node, exit early */ - *ret = NULL; - *ret_size = 0; + if (ret_roothash) + *ret_roothash = NULL; + if (ret_roothash_size) + *ret_roothash_size = 0; + if (ret_verity_data) + *ret_verity_data = NULL; + if (ret_roothashsig) + *ret_roothashsig = NULL; return 0; } - r = getxattr_malloc(image, "user.verity.roothash", &text, true); - if (r < 0) { - char *fn, *e, *n; + if (ret_verity_data) { + char *e; - if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) - return r; - - fn = newa(char, strlen(image) + STRLEN(".roothash") + 1); - n = stpcpy(fn, image); - e = endswith(fn, ".raw"); + verity_filename = new(char, strlen(image) + STRLEN(".verity") + 1); + if (!verity_filename) + return -ENOMEM; + strcpy(verity_filename, image); + e = endswith(verity_filename, ".raw"); if (e) - n = e; + strcpy(e, ".verity"); + else + strcat(verity_filename, ".verity"); - strcpy(n, ".roothash"); - - r = read_one_line_file(fn, &text); - if (r == -ENOENT) { - *ret = NULL; - *ret_size = 0; - return 0; + r = access(verity_filename, F_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + verity_filename = mfree(verity_filename); } - if (r < 0) - return r; } - r = unhexmem(text, strlen(text), &k, &l); - if (r < 0) - return r; - if (l < sizeof(sd_id128_t)) - return -EINVAL; + if (ret_roothashsig) { + char *e; - *ret = TAKE_PTR(k); - *ret_size = l; + /* Follow naming convention recommended by the relevant RFC: + * https://tools.ietf.org/html/rfc5751#section-3.2.1 */ + roothashsig_filename = new(char, strlen(image) + STRLEN(".roothash.p7s") + 1); + if (!roothashsig_filename) + return -ENOMEM; + strcpy(roothashsig_filename, image); + e = endswith(roothashsig_filename, ".raw"); + if (e) + strcpy(e, ".roothash.p7s"); + else + strcat(roothashsig_filename, ".roothash.p7s"); + + r = access(roothashsig_filename, R_OK); + if (r < 0) { + if (errno != ENOENT) + return -errno; + roothashsig_filename = mfree(roothashsig_filename); + } + } + + if (ret_roothash) { + _cleanup_free_ char *text = NULL; + assert(ret_roothash_size); + + if (root_hash_path) { + /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */ + r = read_one_line_file(root_hash_path, &text); + if (r < 0) + return r; + } else { + r = getxattr_malloc(image, "user.verity.roothash", &text, true); + if (r < 0) { + char *fn, *e, *n; + + if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) + return r; + + fn = newa(char, strlen(image) + STRLEN(".roothash") + 1); + n = stpcpy(fn, image); + e = endswith(fn, ".raw"); + if (e) + n = e; + + strcpy(n, ".roothash"); + + r = read_one_line_file(fn, &text); + if (r < 0 && r != -ENOENT) + return r; + } + } + + if (text) { + r = unhexmem(text, strlen(text), &roothash_decoded, &roothash_decoded_size); + if (r < 0) + return r; + if (roothash_decoded_size < sizeof(sd_id128_t)) + return -EINVAL; + } + } + + if (ret_roothash) { + *ret_roothash = TAKE_PTR(roothash_decoded); + *ret_roothash_size = roothash_decoded_size; + } + if (ret_verity_data) + *ret_verity_data = TAKE_PTR(verity_filename); + if (roothashsig_filename) + *ret_roothashsig = TAKE_PTR(roothashsig_filename); return 1; } @@ -1545,14 +1745,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) { fds[2*k+1] = safe_close(fds[2*k+1]); - f = fdopen(fds[2*k], "r"); + f = take_fdopen(&fds[2*k], "r"); if (!f) { r = -errno; goto finish; } - fds[2*k] = -1; - switch (k) { case META_HOSTNAME: @@ -1620,6 +1818,7 @@ int dissect_image_and_warn( const char *name, const void *root_hash, size_t root_hash_size, + const char *verity_data, DissectImageFlags flags, DissectedImage **ret) { @@ -1634,7 +1833,7 @@ int dissect_image_and_warn( name = buffer; } - r = dissect_image(fd, root_hash, root_hash_size, flags, ret); + r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret); switch (r) { @@ -1664,6 +1863,23 @@ int dissect_image_and_warn( } } +bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator) { + if (image->single_file_system) + return partition_designator == PARTITION_ROOT && image->can_verity; + + return PARTITION_VERITY_OF(partition_designator) >= 0; +} + +bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator) { + int k; + + if (image->single_file_system) + return partition_designator == PARTITION_ROOT && image->verity; + + k = PARTITION_VERITY_OF(partition_designator); + return k >= 0 && image->partitions[k].found; +} + static const char *const partition_designator_table[] = { [PARTITION_ROOT] = "root", [PARTITION_ROOT_SECONDARY] = "root-secondary", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 6a666ca7c..84ec1ce33 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -63,12 +63,15 @@ typedef enum DissectImageFlags { DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */ DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */ DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */ + DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */ + DISSECT_IMAGE_VERITY_SHARE = 1 << 13, /* When activating a verity device, reuse existing one if already open */ } DissectImageFlags; struct DissectedImage { bool encrypted:1; bool verity:1; /* verity available and usable */ bool can_verity:1; /* verity available, but not necessarily used */ + bool single_file_system:1; /* MBR/GPT or single file system */ DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX]; @@ -79,14 +82,14 @@ struct DissectedImage { }; int probe_filesystem(const char *node, char **ret_fstype); -int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret); -int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret); +int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret); +int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret); DissectedImage* dissected_image_unref(DissectedImage *m); DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); -int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret); -int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret); +int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret); +int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags); int dissected_image_acquire_metadata(DissectedImage *m); @@ -98,4 +101,6 @@ int decrypted_image_relinquish(DecryptedImage *d); const char* partition_designator_to_string(int i) _const_; int partition_designator_from_string(const char *name) _pure_; -int root_hash_load(const char *image, void **ret, size_t *ret_size); +int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig); +bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator); +bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator); diff --git a/src/shared/dm-util.c b/src/shared/dm-util.c index d817e5b0e..7efcb6b2a 100644 --- a/src/shared/dm-util.c +++ b/src/shared/dm-util.c @@ -6,35 +6,37 @@ #include "fd-util.h" #include "string-util.h" -int dm_deferred_remove(const char *name) { - - struct dm_ioctl dm = { - .version = { - DM_VERSION_MAJOR, - DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL - }, - .data_size = sizeof(dm), - .flags = DM_DEFERRED_REMOVE, - }; - +int dm_deferred_remove_cancel(const char *name) { _cleanup_close_ int fd = -1; + struct message { + struct dm_ioctl dm_ioctl; + struct dm_target_msg dm_target_msg; + char msg_text[STRLEN("@cancel_deferred_remove") + 1]; + } _packed_ message = { + .dm_ioctl = { + .version = { + DM_VERSION_MAJOR, + DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL + }, + .data_size = sizeof(struct message), + .data_start = sizeof(struct dm_ioctl), + }, + .msg_text = "@cancel_deferred_remove", + }; assert(name); - /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() - * directly. */ - - if (strlen(name) >= sizeof(dm.name)) + if (strlen(name) >= sizeof(message.dm_ioctl.name)) return -ENODEV; /* A device with a name longer than this cannot possibly exist */ + strncpy_exact(message.dm_ioctl.name, name, sizeof(message.dm_ioctl.name)); + fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); if (fd < 0) return -errno; - strncpy_exact(dm.name, name, sizeof(dm.name)); - - if (ioctl(fd, DM_DEV_REMOVE, &dm)) + if (ioctl(fd, DM_TARGET_MSG, &message)) return -errno; return 0; diff --git a/src/shared/dm-util.h b/src/shared/dm-util.h index 6c78bfe75..997963c04 100644 --- a/src/shared/dm-util.h +++ b/src/shared/dm-util.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -int dm_deferred_remove(const char *name); +int dm_deferred_remove_cancel(const char *name); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 6844c2b64..932da0c85 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -204,7 +204,7 @@ static int unit_file_find_dirs( type = unit_name_to_type(name); if (type < 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to to derive unit type from unit name: %s", + "Failed to derive unit type from unit name: %s", name); if (is_instance) { @@ -226,30 +226,35 @@ int unit_file_find_dropin_paths( Set *unit_path_cache, const char *dir_suffix, const char *file_suffix, - const Set *names, + const char *name, + const Set *aliases, char ***ret) { _cleanup_strv_free_ char **dirs = NULL; - char *name, **p; + const char *n; + char **p; Iterator i; int r; assert(ret); - SET_FOREACH(name, names, i) + if (name) STRV_FOREACH(p, lookup_path) (void) unit_file_find_dirs(original_root, unit_path_cache, *p, name, dir_suffix, &dirs); + SET_FOREACH(n, aliases, i) + STRV_FOREACH(p, lookup_path) + (void) unit_file_find_dirs(original_root, unit_path_cache, *p, n, dir_suffix, &dirs); + /* All the names in the unit are of the same type so just grab one. */ - name = (char*) set_first(names); - if (name) { + n = name ?: (const char*) set_first(aliases); + if (n) { UnitType type = _UNIT_TYPE_INVALID; - type = unit_name_to_type(name); + type = unit_name_to_type(n); if (type < 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to to derive unit type from unit name: %s", - name); + "Failed to derive unit type from unit name: %s", n); /* Special top level drop in for ".". Add this last as it's the most generic * and should be able to be overridden by more specific drop-ins. */ diff --git a/src/shared/dropin.h b/src/shared/dropin.h index 89a2ab109..addf1dab1 100644 --- a/src/shared/dropin.h +++ b/src/shared/dropin.h @@ -21,5 +21,6 @@ int unit_file_find_dropin_paths( Set *unit_path_cache, const char *dir_suffix, const char *file_suffix, - const Set *names, + const char *name, + const Set *aliases, char ***paths); diff --git a/src/shared/efi-loader.c b/src/shared/efi-loader.c index 12d48f32d..9411fc8ce 100644 --- a/src/shared/efi-loader.c +++ b/src/shared/efi-loader.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include "alloc-util.h" @@ -11,6 +12,7 @@ #include "io-util.h" #include "parse-util.h" #include "sort-util.h" +#include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "utf8.h" @@ -28,10 +30,11 @@ #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[]; \ +#define boot_option__contents \ + { \ + uint32_t attr; \ + uint16_t path_len; \ + uint16_t title[]; \ } struct boot_option boot_option__contents; @@ -49,33 +52,39 @@ struct drive_path { 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; \ - }; \ +#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 (!is_efi_boot()) + if (cache > 0) + return 0; + if (cache == 0) return -EOPNOTSUPP; + if (!is_efi_boot()) + goto not_supported; + r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s); - if (r == -ENOENT) /* variable doesn't exist? it's not supported then */ - return -EOPNOTSUPP; + 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)) @@ -83,36 +92,68 @@ int efi_reboot_to_firmware_supported(void) { b = *(uint64_t*) v; if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) - return -EOPNOTSUPP; /* bit unset? it's not supported then */ + 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 *os_indication) { +static int get_os_indications(uint64_t *ret) { + static struct stat cache_stat = {}; _cleanup_free_ void *v = NULL; + _cleanup_free_ char *fn = 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; + fn = efi_variable_path(EFI_VENDOR_GLOBAL, "OsIndications"); + if (!fn) + return -ENOMEM; + + /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */ + if (stat(fn, &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_VENDOR_GLOBAL, "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. */ - *os_indication = 0; + * 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; - } else if (r < 0) + } + if (r < 0) return r; - else if (s != sizeof(uint64_t)) + if (s != sizeof(uint64_t)) return -EINVAL; - *os_indication = *(uint64_t *)v; + cache_stat = new_stat; + *ret = cache = *(uint64_t *)v; return 0; } @@ -135,10 +176,7 @@ int efi_set_reboot_to_firmware(bool value) { if (r < 0) return r; - if (value) - b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - else - b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + 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) @@ -675,6 +713,76 @@ int efi_loader_get_features(uint64_t *ret) { return 0; } +int efi_loader_get_config_timeout_one_shot(usec_t *ret) { + _cleanup_free_ char *v = NULL, *fn = NULL; + static struct stat cache_stat = {}; + struct stat new_stat; + static usec_t cache; + uint64_t sec; + int r; + + assert(ret); + + fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot"); + if (!fn) + return -ENOMEM; + + /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */ + if (stat(fn, &new_stat) < 0) + return -errno; + + if (stat_inode_unmodified(&new_stat, &cache_stat)) { + *ret = cache; + return 0; + } + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v); + if (r < 0) + return r; + + r = safe_atou64(v, &sec); + if (r < 0) + return r; + if (sec > USEC_INFINITY / USEC_PER_SEC) + return -ERANGE; + + cache_stat = new_stat; + *ret = cache = sec * USEC_PER_SEC; /* return in µs */ + return 0; +} + +int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) { + _cleanup_free_ char *fn = NULL, *v = NULL; + struct stat new_stat; + int r; + + assert(cache); + assert(cache_stat); + + fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntryOneShot"); + if (!fn) + return -ENOMEM; + + /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */ + if (stat(fn, &new_stat) < 0) + return -errno; + + if (stat_inode_unmodified(&new_stat, cache_stat)) + return 0; + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v); + if (r < 0) + return r; + + if (!efi_loader_entry_name_valid(v)) + return -EINVAL; + + *cache_stat = new_stat; + free_and_replace(*cache, v); + + return 0; +} + #endif bool efi_loader_entry_name_valid(const char *s) { diff --git a/src/shared/efi-loader.h b/src/shared/efi-loader.h index 96208d25b..171274a0e 100644 --- a/src/shared/efi-loader.h +++ b/src/shared/efi-loader.h @@ -3,6 +3,8 @@ #include "efivars.h" +#include + #if ENABLE_EFI int efi_reboot_to_firmware_supported(void); @@ -23,6 +25,9 @@ int efi_loader_get_entries(char ***ret); 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); + #else static inline int efi_reboot_to_firmware_supported(void) { @@ -77,6 +82,14 @@ static inline int efi_loader_get_features(uint64_t *ret) { return -EOPNOTSUPP; } +static inline int efi_loader_get_config_timeout_one_shot(usec_t *ret) { + return -EOPNOTSUPP; +} + +static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) { + return -EOPNOTSUPP; +} + #endif bool efi_loader_entry_name_valid(const char *s); diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index c985b5aea..3bb12f922 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -441,6 +441,16 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, netdev_ring need_update = true; } + if (ring->rx_mini_pending_set && ecmd.rx_mini_pending != ring->rx_mini_pending) { + ecmd.rx_mini_pending = ring->rx_mini_pending; + need_update = true; + } + + if (ring->rx_jumbo_pending_set && ecmd.rx_jumbo_pending != ring->rx_jumbo_pending) { + ecmd.rx_jumbo_pending = ring->rx_jumbo_pending; + need_update = true; + } + if (ring->tx_pending_set && ecmd.tx_pending != ring->tx_pending) { ecmd.tx_pending = ring->tx_pending; need_update = true; @@ -857,6 +867,55 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) return 0; } +int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) { + struct ethtool_pauseparam ecmd = { + .cmd = ETHTOOL_GPAUSEPARAM + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + + bool need_update = false; + int r; + + if (*fd < 0) { + r = ethtool_connect_or_warn(fd, true); + if (r < 0) + return r; + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) { + ecmd.rx_pause = rx; + need_update = true; + } + + if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) { + ecmd.tx_pause = tx; + need_update = true; + } + + if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) { + ecmd.autoneg = autoneg; + need_update = true; + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SPAUSEPARAM; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} + int config_parse_channel(const char *unit, const char *filename, unsigned line, @@ -993,6 +1052,12 @@ int config_parse_nic_buffer_size(const char *unit, if (streq(lvalue, "RxBufferSize")) { ring->rx_pending = k; ring->rx_pending_set = true; + } else if (streq(lvalue, "RxMiniBufferSize")) { + ring->rx_mini_pending = k; + ring->rx_mini_pending_set = true; + } else if (streq(lvalue, "RxJumboBufferSize")) { + ring->rx_jumbo_pending = k; + ring->rx_jumbo_pending_set = true; } else if (streq(lvalue, "TxBufferSize")) { ring->tx_pending = k; ring->tx_pending_set = true; diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index c1d5d7590..473024170 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -84,9 +84,13 @@ typedef struct netdev_channels { typedef struct netdev_ring_param { uint32_t rx_pending; + uint32_t rx_mini_pending; + uint32_t rx_jumbo_pending; uint32_t tx_pending; bool rx_pending_set; + bool rx_mini_pending_set; + bool rx_jumbo_pending_set; bool tx_pending_set; } netdev_ring_param; @@ -103,6 +107,7 @@ int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname, int autonegotiation, uint32_t advertise[static N_ADVERTISE], uint64_t speed, Duplex duplex, NetDevPort port); int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels); +int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg); const char *duplex_to_string(Duplex d) _const_; Duplex duplex_from_string(const char *d) _pure_; diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 64aa2b68e..87ef5c3f0 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -73,6 +73,7 @@ typedef struct TableData { bool uppercase; /* Uppercase string on display */ const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */ + const char *rgap_color; /* The ANSI color to use for the gap right of this cell. Usually used to underline entire rows in a gapless fashion */ char *url; /* A URL to use for a clickable hyperlink */ char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */ @@ -334,7 +335,7 @@ static bool table_data_matches( return false; /* If a color/url/uppercase flag is set, refuse to merge */ - if (d->color) + if (d->color || d->rgap_color) return false; if (d->url) return false; @@ -542,6 +543,7 @@ static int table_dedup_cell(Table *t, TableCell *cell) { return -ENOMEM; nd->color = od->color; + nd->rgap_color = od->rgap_color; nd->url = TAKE_PTR(curl); nd->uppercase = od->uppercase; @@ -671,6 +673,20 @@ int table_set_color(Table *t, TableCell *cell, const char *color) { return 0; } +int table_set_rgap_color(Table *t, TableCell *cell, const char *color) { + int r; + + assert(t); + assert(cell); + + r = table_dedup_cell(t, cell); + if (r < 0) + return r; + + table_get_data(t, cell)->rgap_color = empty_to_null(color); + return 0; +} + int table_set_url(Table *t, TableCell *cell, const char *url) { _cleanup_free_ char *copy = NULL; int r; @@ -744,6 +760,7 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data return -ENOMEM; nd->color = od->color; + nd->rgap_color = od->rgap_color; nd->url = TAKE_PTR(curl); nd->uppercase = od->uppercase; @@ -952,6 +969,25 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { break; } + case TABLE_SET_RGAP_COLOR: { + const char *c = va_arg(ap, const char*); + r = table_set_rgap_color(t, last_cell, c); + break; + } + + case TABLE_SET_BOTH_COLORS: { + const char *c = va_arg(ap, const char*); + + r = table_set_color(t, last_cell, c); + if (r < 0) { + va_end(ap); + return r; + } + + r = table_set_rgap_color(t, last_cell, c); + break; + } + case TABLE_SET_URL: { const char *u = va_arg(ap, const char*); r = table_set_url(t, last_cell, u); @@ -1241,7 +1277,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) { return CMP(*a, *b); } -static const char *table_data_format(Table *t, TableData *d) { +static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) { assert(d); if (d->formatted) @@ -1253,7 +1289,7 @@ static const char *table_data_format(Table *t, TableData *d) { case TABLE_STRING: case TABLE_PATH: - if (d->uppercase) { + if (d->uppercase && !avoid_uppercasing) { char *p, *q; d->formatted = new(char, strlen(d->string) + 1); @@ -1272,6 +1308,9 @@ static const char *table_data_format(Table *t, TableData *d) { case TABLE_STRV: { char *p; + if (strv_isempty(d->strv)) + return strempty(t->empty_string); + p = strv_join(d->strv, "\n"); if (!p) return NULL; @@ -1602,7 +1641,7 @@ static int table_data_requested_width_height( const char *t; int r; - t = table_data_format(table, d); + t = table_data_format(table, d, false); if (!t) return -ENOMEM; @@ -1694,6 +1733,20 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt return ret; } +static bool table_data_isempty(TableData *d) { + assert(d); + + if (d->type == TABLE_EMPTY) + return true; + + /* Let's also consider an empty strv as truly empty. */ + if (d->type == TABLE_STRV) + return strv_isempty(d->strv); + + /* Note that an empty string we do not consider empty here! */ + return false; +} + static const char* table_data_color(TableData *d) { assert(d); @@ -1701,12 +1754,21 @@ static const char* table_data_color(TableData *d) { return d->color; /* Let's implicitly color all "empty" cells in grey, in case an "empty_string" is set that is not empty */ - if (d->type == TABLE_EMPTY) + if (table_data_isempty(d)) return ansi_grey(); return NULL; } +static const char* table_data_rgap_color(TableData *d) { + assert(d); + + if (d->rgap_color) + return d->rgap_color; + + return NULL; +} + int table_print(Table *t, FILE *f) { size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width, i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width, @@ -1784,7 +1846,7 @@ int table_print(Table *t, FILE *f) { * ellipsis. Hence, let's figure out the last line, and account for its * length plus ellipsis. */ - field = table_data_format(t, d); + field = table_data_format(t, d, false); if (!field) return -ENOMEM; @@ -1958,18 +2020,19 @@ int table_print(Table *t, FILE *f) { row = t->data + i * t->n_columns; do { + const char *gap_color = NULL; more_sublines = false; for (j = 0; j < display_columns; j++) { _cleanup_free_ char *buffer = NULL, *extracted = NULL; bool lines_truncated = false; - const char *field; + const char *field, *color = NULL; TableData *d; size_t l; assert_se(d = row[t->display_map ? t->display_map[j] : j]); - field = table_data_format(t, d); + field = table_data_format(t, d, false); if (!field) return -ENOMEM; @@ -2001,7 +2064,7 @@ int table_print(Table *t, FILE *f) { _cleanup_free_ char *padded = NULL; /* We truncated more lines of this cell, let's add an - * ellipsis. We first append it, but thta might make our + * ellipsis. We first append it, but that might make our * string grow above what we have space for, hence ellipsize * right after. This will truncate the ellipsis and add a new * one. */ @@ -2042,23 +2105,35 @@ int table_print(Table *t, FILE *f) { field = buffer; } - if (row == t->data) /* underline header line fully, including the column separator */ - fputs(ansi_underline(), f); + if (colors_enabled()) { + if (gap_color) + fputs(gap_color, f); + else if (row == t->data) /* underline header line fully, including the column separator */ + fputs(ansi_underline(), f); + } if (j > 0) - fputc(' ', f); /* column separator */ + fputc(' ', f); /* column separator left of cell */ - if (table_data_color(d) && colors_enabled()) { - if (row == t->data) /* first undo header underliner */ + if (colors_enabled()) { + color = table_data_color(d); + + /* Undo gap color */ + if (gap_color || (color && row == t->data)) fputs(ANSI_NORMAL, f); - fputs(table_data_color(d), f); + if (color) + fputs(color, f); + else if (gap_color && row == t->data) /* underline header line cell */ + fputs(ansi_underline(), f); } fputs(field, f); - if (colors_enabled() && (table_data_color(d) || row == t->data)) + if (colors_enabled() && (color || row == t->data)) fputs(ANSI_NORMAL, f); + + gap_color = table_data_rgap_color(d); } fputc('\n', f); @@ -2256,6 +2331,24 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { } } +static char* string_to_json_field_name(const char *f) { + char *c, *x; + + /* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a + * field name can be hence this is a bit vague and black magic. Right now we only convert spaces to + * underscores and leave everything as is. */ + + c = strdup(f); + if (!c) + return NULL; + + for (x = c; *x; x++) + if (isspace(*x)) + *x = '_'; + + return c; +} + int table_to_json(Table *t, JsonVariant **ret) { JsonVariant **rows = NULL, **elements = NULL; _cleanup_free_ size_t *sorted = NULL; @@ -2298,11 +2391,27 @@ int table_to_json(Table *t, JsonVariant **ret) { } for (j = 0; j < display_columns; j++) { + _cleanup_free_ char *mangled = NULL; + const char *formatted; TableData *d; assert_se(d = t->data[t->display_map ? t->display_map[j] : j]); - r = table_data_to_json(d, elements + j*2); + /* Field names must be strings, hence format whatever we got here as a string first */ + formatted = table_data_format(t, d, true); + if (!formatted) { + r = -ENOMEM; + goto finish; + } + + /* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */ + mangled = string_to_json_field_name(formatted); + if (!mangled) { + r = -ENOMEM; + goto finish; + } + + r = json_variant_new_string(elements + j*2, mangled); if (r < 0) goto finish; } diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 62f1ed740..1851f1d14 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -47,6 +47,8 @@ typedef enum TableDataType { TABLE_SET_ALIGN_PERCENT, TABLE_SET_ELLIPSIZE_PERCENT, TABLE_SET_COLOR, + TABLE_SET_RGAP_COLOR, + TABLE_SET_BOTH_COLORS, TABLE_SET_URL, TABLE_SET_UPPERCASE, @@ -89,6 +91,7 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight); int table_set_align_percent(Table *t, TableCell *cell, unsigned percent); int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); int table_set_color(Table *t, TableCell *cell, const char *color); +int table_set_rgap_color(Table *t, TableCell *cell, const char *color); int table_set_url(Table *t, TableCell *cell, const char *url); int table_set_uppercase(Table *t, TableCell *cell, bool b); @@ -127,3 +130,9 @@ int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags); #define table_log_add_error(r) \ log_error_errno(r, "Failed to add cell(s) to table: %m") + +#define table_log_print_error(r) \ + log_error_errno(r, "Failed to print table: %m") + +#define table_log_sort_error(r) \ + log_error_errno(r, "Failed to sort table: %m") diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index b19127be0..806dda847 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -80,7 +80,7 @@ int fstab_is_mount_point(const char *mount) { } int fstab_filter_options(const char *opts, const char *names, - const char **namefound, char **value, char **filtered) { + const char **ret_namefound, char **ret_value, char **ret_filtered) { const char *name, *n = NULL, *x; _cleanup_strv_free_ char **stor = NULL; _cleanup_free_ char *v = NULL, **strv = NULL; @@ -92,7 +92,7 @@ int fstab_filter_options(const char *opts, const char *names, /* If !value and !filtered, this function is not allowed to fail. */ - if (!filtered) { + if (!ret_filtered) { const char *word, *state; size_t l; @@ -108,7 +108,7 @@ int fstab_filter_options(const char *opts, const char *names, x = word + strlen(name); if (IN_SET(*x, '\0', '=', ',')) { n = name; - if (value) { + if (ret_value) { free(v); if (IN_SET(*x, '\0', ',')) v = NULL; @@ -145,7 +145,7 @@ int fstab_filter_options(const char *opts, const char *names, found: /* Keep the last occurrence found */ n = name; - if (value) { + if (ret_value) { free(v); if (*x == '\0') v = NULL; @@ -162,19 +162,19 @@ int fstab_filter_options(const char *opts, const char *names, } answer: - if (namefound) - *namefound = n; - if (filtered) { + if (ret_namefound) + *ret_namefound = n; + if (ret_filtered) { char *f; f = strv_join(strv, ","); if (!f) return -ENOMEM; - *filtered = f; + *ret_filtered = f; } - if (value) - *value = TAKE_PTR(v); + if (ret_value) + *ret_value = TAKE_PTR(v); return !!n; } diff --git a/src/shared/geneve-util.c b/src/shared/geneve-util.c new file mode 100644 index 000000000..fad01e208 --- /dev/null +++ b/src/shared/geneve-util.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "geneve-util.h" +#include "string-table.h" + +static const char* const geneve_df_table[_NETDEV_GENEVE_DF_MAX] = { + [NETDEV_GENEVE_DF_UNSET] = "unset", + [NETDEV_GENEVE_DF_SET] = "set", + [NETDEV_GENEVE_DF_INHERIT] = "inherit", +}; + +DEFINE_STRING_TABLE_LOOKUP(geneve_df, GeneveDF); diff --git a/src/shared/geneve-util.h b/src/shared/geneve-util.h new file mode 100644 index 000000000..63c03ae02 --- /dev/null +++ b/src/shared/geneve-util.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "conf-parser.h" + +typedef enum GeneveDF { + NETDEV_GENEVE_DF_UNSET = GENEVE_DF_UNSET, + NETDEV_GENEVE_DF_SET = GENEVE_DF_SET, + NETDEV_GENEVE_DF_INHERIT = GENEVE_DF_INHERIT, + _NETDEV_GENEVE_DF_MAX, + _NETDEV_GENEVE_DF_INVALID = -1, +} GeneveDF; + +const char *geneve_df_to_string(GeneveDF d) _const_; +GeneveDF geneve_df_from_string(const char *d) _pure_; diff --git a/src/shared/group-record-nss.c b/src/shared/group-record-nss.c index 77924f1c4..5c4fae865 100644 --- a/src/shared/group-record-nss.c +++ b/src/shared/group-record-nss.c @@ -106,12 +106,16 @@ int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **re } } -int nss_group_record_by_name(const char *name, GroupRecord **ret) { +int nss_group_record_by_name( + const char *name, + bool with_shadow, + GroupRecord **ret) { + _cleanup_free_ char *buf = NULL, *sbuf = NULL; struct group grp, *result; bool incomplete = false; size_t buflen = 4096; - struct sgrp sgrp; + struct sgrp sgrp, *sresult = NULL; int r; assert(name); @@ -141,13 +145,17 @@ int nss_group_record_by_name(const char *name, GroupRecord **ret) { buf = mfree(buf); } - r = nss_sgrp_for_group(result, &sgrp, &sbuf); - if (r < 0) { - log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); - incomplete = ERRNO_IS_PRIVILEGE(r); - } + if (with_shadow) { + r = nss_sgrp_for_group(result, &sgrp, &sbuf); + if (r < 0) { + log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); + incomplete = ERRNO_IS_PRIVILEGE(r); + } else + sresult = &sgrp; + } else + incomplete = true; - r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret); + r = nss_group_to_group_record(result, sresult, ret); if (r < 0) return r; @@ -155,12 +163,16 @@ int nss_group_record_by_name(const char *name, GroupRecord **ret) { return 0; } -int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) { +int nss_group_record_by_gid( + gid_t gid, + bool with_shadow, + GroupRecord **ret) { + _cleanup_free_ char *buf = NULL, *sbuf = NULL; struct group grp, *result; bool incomplete = false; size_t buflen = 4096; - struct sgrp sgrp; + struct sgrp sgrp, *sresult = NULL; int r; assert(ret); @@ -188,13 +200,17 @@ int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) { buf = mfree(buf); } - r = nss_sgrp_for_group(result, &sgrp, &sbuf); - if (r < 0) { - log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); - incomplete = ERRNO_IS_PRIVILEGE(r); - } + if (with_shadow) { + r = nss_sgrp_for_group(result, &sgrp, &sbuf); + if (r < 0) { + log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); + incomplete = ERRNO_IS_PRIVILEGE(r); + } else + sresult = &sgrp; + } else + incomplete = true; - r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret); + r = nss_group_to_group_record(result, sresult, ret); if (r < 0) return r; diff --git a/src/shared/group-record-nss.h b/src/shared/group-record-nss.h index 38b299517..077c22d89 100644 --- a/src/shared/group-record-nss.h +++ b/src/shared/group-record-nss.h @@ -11,5 +11,5 @@ int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret); int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer); -int nss_group_record_by_name(const char *name, GroupRecord **ret); -int nss_group_record_by_gid(gid_t gid, GroupRecord **ret); +int nss_group_record_by_name(const char *name, bool with_shadow, GroupRecord **ret); +int nss_group_record_by_gid(gid_t gid, bool with_shadow, GroupRecord **ret); diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 2061384af..9267d52b9 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -115,7 +115,7 @@ int install_full_printf(const UnitFileInstallInfo *i, const char *format, char * * %U the UID of the running user * %u the username of running user * %m the machine ID of the running system - * %H the host name of the running system + * %H the hostname of the running system * %b the boot ID of the running system * %v `uname -r` of the running system */ @@ -133,9 +133,14 @@ int install_full_printf(const UnitFileInstallInfo *i, const char *format, char * { 'u', specifier_user_name, NULL }, { 'm', specifier_machine_id, NULL }, - { 'H', specifier_host_name, NULL }, { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, {} }; diff --git a/src/shared/install.c b/src/shared/install.c index 78532573c..3d1938750 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -55,16 +55,11 @@ typedef enum { PRESET_DISABLE, } PresetAction; -typedef struct { +struct UnitFilePresetRule { char *pattern; PresetAction action; char **instances; -} PresetRule; - -typedef struct { - PresetRule *rules; - size_t n_rules; -} Presets; +}; static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) { assert(i); @@ -80,7 +75,7 @@ static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) { return !strv_isempty(i->also); } -static void presets_freep(Presets *p) { +void unit_file_presets_freep(UnitFilePresets *p) { size_t i; if (!p) @@ -259,7 +254,7 @@ static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) { int unit_file_changes_add( UnitFileChange **changes, size_t *n_changes, - UnitFileChangeType type, + int type, const char *path, const char *source) { @@ -1241,8 +1236,7 @@ static int unit_file_load( "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type)); if (!(flags & SEARCH_LOAD)) { - r = lstat(path, &st); - if (r < 0) + if (lstat(path, &st) < 0) return -errno; if (null_or_empty(&st)) @@ -1287,10 +1281,9 @@ static int unit_file_load( if (r < 0) return r; - f = fdopen(fd, "r"); + f = take_fdopen(&fd, "r"); if (!f) return -errno; - fd = -1; /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ assert(c); @@ -1310,7 +1303,8 @@ static int unit_file_load( "-Target\0" "-Timer\0", config_item_table_lookup, items, - CONFIG_PARSE_ALLOW_INCLUDE, info); + 0, info, + NULL); if (r < 0) return log_debug_errno(r, "Failed to parse %s: %m", info->name); @@ -1329,26 +1323,40 @@ static int unit_file_load_or_readlink( const char *path, const char *root_dir, SearchFlags flags) { - - _cleanup_free_ char *target = NULL; + _cleanup_free_ char *resolved = NULL; + struct stat st; int r; r = unit_file_load(c, info, path, root_dir, flags); if (r != -ELOOP || (flags & SEARCH_DROPIN)) return r; - /* This is a symlink, let's read it. */ - - r = readlink_malloc(path, &target); - if (r < 0) - return r; - - if (path_equal(target, "/dev/null")) + 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. */ info->type = UNIT_FILE_TYPE_MASKED; + + else if (r > 0 && + stat(resolved, &st) >= 0 && + null_or_empty(&st)) + + 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)) { @@ -1719,7 +1727,7 @@ 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 occured. + * distinguish the case where instance propagation occurred. */ const char *path_alias = strrchr(dst, '/'); @@ -2780,6 +2788,12 @@ int unit_file_lookup_state( break; 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)) { + state = UNIT_FILE_ALIAS; + break; + } + r = path_is_generator(paths, i->path); if (r < 0) return r; @@ -2921,8 +2935,8 @@ 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, Presets *presets) { - _cleanup_(presets_freep) Presets ps = {}; +static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) { + _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {}; size_t n_allocated = 0; _cleanup_strv_free_ char **files = NULL; char **p; @@ -2950,7 +2964,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres for (;;) { _cleanup_free_ char *line = NULL; - PresetRule rule = {}; + UnitFilePresetRule rule = {}; const char *parameter; char *l; @@ -2980,7 +2994,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres continue; } - rule = (PresetRule) { + rule = (UnitFilePresetRule) { .pattern = unit_name, .action = PRESET_ENABLE, .instances = instances, @@ -2995,7 +3009,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres if (!pattern) return -ENOMEM; - rule = (PresetRule) { + rule = (UnitFilePresetRule) { .pattern = pattern, .action = PRESET_DISABLE, }; @@ -3013,14 +3027,15 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres } } + ps.initialized = true; *presets = ps; - ps = (Presets){}; + ps = (UnitFilePresets){}; return 0; } static int pattern_match_multiple_instances( - const PresetRule rule, + const UnitFilePresetRule rule, const char *unit_name, char ***ret) { @@ -3074,17 +3089,17 @@ static int pattern_match_multiple_instances( return 0; } -static int query_presets(const char *name, const Presets presets, char ***instance_name_list) { +static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) { PresetAction action = PRESET_UNKNOWN; size_t i; char **s; if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - for (i = 0; i < presets.n_rules; i++) - if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 || - fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { - action = presets.rules[i].action; + for (i = 0; i < presets->n_rules; i++) + if (pattern_match_multiple_instances(presets->rules[i], name, instance_name_list) > 0 || + fnmatch(presets->rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets->rules[i].action; break; } @@ -3107,15 +3122,19 @@ static int query_presets(const char *name, const Presets presets, char ***instan } } -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { - _cleanup_(presets_freep) Presets presets = {}; +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { + _cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {}; int r; - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; + if (!cached) + cached = &tmp; + if (!cached->initialized) { + r = read_presets(scope, root_dir, cached); + if (r < 0) + return r; + } - return query_presets(name, presets, NULL); + return query_presets(name, cached, NULL); } static int execute_preset( @@ -3170,7 +3189,7 @@ static int preset_prepare_one( InstallContext *minus, LookupPaths *paths, const char *name, - Presets presets, + const UnitFilePresets *presets, UnitFileChange **changes, size_t *n_changes) { @@ -3229,7 +3248,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path; char **i; int r; @@ -3251,7 +3270,7 @@ 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, &paths, *i, &presets, changes, n_changes); if (r < 0) return r; } @@ -3269,7 +3288,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path = NULL; char **i; int r; @@ -3313,7 +3332,7 @@ int unit_file_preset_all( continue; /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0); + r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, &presets, NULL, 0); if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); @@ -3352,7 +3371,7 @@ int unit_file_get_list( char **patterns) { _cleanup_(lookup_paths_free) LookupPaths paths = {}; - char **i; + char **dirname; int r; assert(scope >= 0); @@ -3363,16 +3382,16 @@ int unit_file_get_list( if (r < 0) return r; - STRV_FOREACH(i, paths.search_path) { + STRV_FOREACH(dirname, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - d = opendir(*i); + d = opendir(*dirname); if (!d) { if (errno == ENOENT) continue; if (IN_SET(errno, ENOTDIR, EACCES)) { - log_debug_errno(errno, "Failed to open \"%s\": %m", *i); + log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname); continue; } @@ -3400,7 +3419,7 @@ int unit_file_get_list( if (!f) return -ENOMEM; - f->path = path_make_absolute(de->d_name, *i); + f->path = path_make_absolute(de->d_name, *dirname); if (!f->path) return -ENOMEM; @@ -3424,34 +3443,35 @@ int unit_file_get_list( } static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { - [UNIT_FILE_ENABLED] = "enabled", + [UNIT_FILE_ENABLED] = "enabled", [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime", - [UNIT_FILE_LINKED] = "linked", - [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", - [UNIT_FILE_MASKED] = "masked", - [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", - [UNIT_FILE_STATIC] = "static", - [UNIT_FILE_DISABLED] = "disabled", - [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_GENERATED] = "generated", - [UNIT_FILE_TRANSIENT] = "transient", - [UNIT_FILE_BAD] = "bad", + [UNIT_FILE_LINKED] = "linked", + [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", + [UNIT_FILE_ALIAS] = "alias", + [UNIT_FILE_MASKED] = "masked", + [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", + [UNIT_FILE_STATIC] = "static", + [UNIT_FILE_DISABLED] = "disabled", + [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", + [UNIT_FILE_BAD] = "bad", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { - [UNIT_FILE_SYMLINK] = "symlink", - [UNIT_FILE_UNLINK] = "unlink", - [UNIT_FILE_IS_MASKED] = "masked", + [UNIT_FILE_SYMLINK] = "symlink", + [UNIT_FILE_UNLINK] = "unlink", + [UNIT_FILE_IS_MASKED] = "masked", [UNIT_FILE_IS_DANGLING] = "dangling", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { - [UNIT_FILE_PRESET_FULL] = "full", - [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", + [UNIT_FILE_PRESET_FULL] = "full", + [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only", }; diff --git a/src/shared/install.h b/src/shared/install.h index 54d22a45d..788517d23 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -183,13 +183,22 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char * int unit_file_get_list(UnitFileScope 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, UnitFileChangeType type, const char *path, const char *source); +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_query_preset(UnitFileScope scope, const char *root_dir, const char *name); +typedef struct UnitFilePresetRule UnitFilePresetRule; + +typedef struct { + UnitFilePresetRule *rules; + size_t n_rules; + bool initialized; +} UnitFilePresets; + +void unit_file_presets_freep(UnitFilePresets *p); +int unit_file_query_preset(UnitFileScope 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/ipvlan-util.c b/src/shared/ipvlan-util.c new file mode 100644 index 000000000..da6be7678 --- /dev/null +++ b/src/shared/ipvlan-util.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "ipvlan-util.h" +#include "string-table.h" + +static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = { + [NETDEV_IPVLAN_MODE_L2] = "L2", + [NETDEV_IPVLAN_MODE_L3] = "L3", + [NETDEV_IPVLAN_MODE_L3S] = "L3S", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode); + +static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = { + [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge", + [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private", + [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags); diff --git a/src/shared/ipvlan-util.h b/src/shared/ipvlan-util.h new file mode 100644 index 000000000..dda659d42 --- /dev/null +++ b/src/shared/ipvlan-util.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#include "macro.h" + +typedef enum IPVlanMode { + NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, + NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3, + NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S, + _NETDEV_IPVLAN_MODE_MAX, + _NETDEV_IPVLAN_MODE_INVALID = -1 +} IPVlanMode; + +typedef enum IPVlanFlags { + NETDEV_IPVLAN_FLAGS_BRIGDE, + NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE, + NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA, + _NETDEV_IPVLAN_FLAGS_MAX, + _NETDEV_IPVLAN_FLAGS_INVALID = -1 +} IPVlanFlags; + +const char *ipvlan_mode_to_string(IPVlanMode d) _const_; +IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; + +const char *ipvlan_flags_to_string(IPVlanFlags d) _const_; +IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_; diff --git a/src/shared/json.c b/src/shared/json.c index 7a79acdee..27a3a518f 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -253,10 +253,9 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) { return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v; case JSON_VARIANT_REAL: -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v; -#pragma GCC diagnostic pop + REENABLE_WARNING; case JSON_VARIANT_STRING: return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v; @@ -353,13 +352,12 @@ int json_variant_new_real(JsonVariant **ret, long double d) { assert_return(ret, -EINVAL); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; if (d == 0.0) { -#pragma GCC diagnostic pop *ret = JSON_VARIANT_MAGIC_ZERO_REAL; return 0; } + REENABLE_WARNING; r = json_variant_new(&v, JSON_VARIANT_REAL, sizeof(d)); if (r < 0) @@ -896,11 +894,10 @@ intmax_t json_variant_integer(JsonVariant *v) { converted = (intmax_t) v->value.real; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; if ((long double) converted == v->value.real) -#pragma GCC diagnostic pop return converted; + REENABLE_WARNING; log_debug("Real %Lg requested as integer, and cannot be converted losslessly, returning 0.", v->value.real); return 0; @@ -944,11 +941,10 @@ uintmax_t json_variant_unsigned(JsonVariant *v) { converted = (uintmax_t) v->value.real; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; if ((long double) converted == v->value.real) -#pragma GCC diagnostic pop return converted; + REENABLE_WARNING; log_debug("Real %Lg requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real); return 0; @@ -1097,9 +1093,12 @@ JsonVariantType json_variant_type(JsonVariant *v) { return v->type; } -bool json_variant_has_type(JsonVariant *v, JsonVariantType type) { +_function_no_sanitize_float_cast_overflow_ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) { JsonVariantType rt; + /* Note: we turn off ubsan float cast overflo detection for this function, since it would complain + * about our float casts but we do them explicitly to detect conversion errors. */ + v = json_variant_dereference(v); if (!v) return false; @@ -1137,14 +1136,15 @@ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) { if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL) return (uintmax_t) (long double) v->value.unsig == v->value.unsig; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; + /* Any real that can be converted losslessly to an integer and back may also be considered an integer */ if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER) return (long double) (intmax_t) v->value.real == v->value.real; if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED) return (long double) (uintmax_t) v->value.real == v->value.real; -#pragma GCC diagnostic pop + + REENABLE_WARNING; return false; } @@ -1298,10 +1298,9 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) { return json_variant_unsigned(a) == json_variant_unsigned(b); case JSON_VARIANT_REAL: -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; return json_variant_real(a) == json_variant_real(b); -#pragma GCC diagnostic pop + REENABLE_WARNING; case JSON_VARIANT_BOOLEAN: return json_variant_boolean(a) == json_variant_boolean(b); @@ -1381,7 +1380,7 @@ void json_variant_sensitive(JsonVariant *v) { /* Marks a variant as "sensitive", so that it is erased from memory when it is destroyed. This is a * one-way operation: as soon as it is marked this way it remains marked this way until it's - * destoryed. A magic variant is never sensitive though, even when asked, since it's too + * destroyed. A magic variant is never sensitive though, even when asked, since it's too * basic. Similar, const string variant are never sensitive either, after all they are included in * the source code as they are, which is not suitable for inclusion of secrets. * @@ -1396,6 +1395,19 @@ void json_variant_sensitive(JsonVariant *v) { v->sensitive = true; } +bool json_variant_is_sensitive(JsonVariant *v) { + v = json_variant_formalize(v); + if (!json_variant_is_regular(v)) + return false; + + return v->sensitive; +} + +static void json_variant_propagate_sensitive(JsonVariant *from, JsonVariant *to) { + if (json_variant_is_sensitive(from)) + json_variant_sensitive(to); +} + int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column) { assert_return(v, -EINVAL); @@ -1829,6 +1841,8 @@ int json_variant_filter(JsonVariant **v, char **to_remove) { if (r < 0) return r; + json_variant_propagate_sensitive(*v, w); + json_variant_unref(*v); *v = TAKE_PTR(w); @@ -1898,6 +1912,8 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu if (r < 0) return r; + json_variant_propagate_sensitive(*v, w); + json_variant_unref(*v); *v = TAKE_PTR(w); @@ -2005,6 +2021,9 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) { if (r < 0) return r; + json_variant_propagate_sensitive(*v, w); + json_variant_propagate_sensitive(m, w); + json_variant_unref(*v); *v = TAKE_PTR(w); @@ -2044,10 +2063,11 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) { r = json_variant_new_array(&nv, array, i + 1); } - if (r < 0) return r; + json_variant_propagate_sensitive(*v, nv); + json_variant_unref(*v); *v = TAKE_PTR(nv); @@ -2193,6 +2213,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) { memcpy_safe(&c->value, source, k); + json_variant_propagate_sensitive(v, c); + *nv = c; return 0; } @@ -4072,10 +4094,9 @@ int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFl assert_cc(sizeof(uid_t) == sizeof(uint32_t)); assert_cc(sizeof(gid_t) == sizeof(uint32_t)); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" + DISABLE_WARNING_TYPE_LIMITS; assert_cc(((uid_t) -1 < (uid_t) 0) == ((gid_t) -1 < (gid_t) 0)); -#pragma GCC diagnostic pop + REENABLE_WARNING; if (json_variant_is_null(variant)) { *uid = UID_INVALID; @@ -4178,6 +4199,9 @@ int json_variant_sort(JsonVariant **v) { r = json_variant_new_object(&n, a, m); if (r < 0) return r; + + json_variant_propagate_sensitive(*v, n); + if (!n->sorted) /* Check if this worked. This will fail if there are multiple identical keys used. */ return -ENOTUNIQ; @@ -4226,6 +4250,9 @@ int json_variant_normalize(JsonVariant **v) { } if (r < 0) goto finish; + + json_variant_propagate_sensitive(*v, n); + if (!n->normalized) { /* Let's see if normalization worked. It will fail if there are multiple * identical keys used in the same object anywhere, or if there are floating * point numbers used (see below) */ diff --git a/src/shared/json.h b/src/shared/json.h index a4e5b6f50..ceb01a202 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -135,6 +135,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria bool json_variant_equal(JsonVariant *a, JsonVariant *b); void json_variant_sensitive(JsonVariant *v); +bool json_variant_is_sensitive(JsonVariant *v); struct json_variant_foreach_state { JsonVariant *variant; diff --git a/src/shared/linux/nl80211.h b/src/shared/linux/nl80211.h index beee59c83..b065c1fe4 100644 --- a/src/shared/linux/nl80211.h +++ b/src/shared/linux/nl80211.h @@ -1435,7 +1435,7 @@ enum nl80211_commands { * rates as defined by IEEE 802.11 7.3.2.2 but without the length * restriction (at most %NL80211_MAX_SUPP_RATES). * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station - * to, or the AP interface the station was originally added to to. + * to, or the AP interface the station was originally added to. * @NL80211_ATTR_STA_INFO: information about a station, part of station info * given for %NL80211_CMD_GET_STATION, nested attribute containing * info as possible, see &enum nl80211_sta_info. diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 2bfd0b60c..899e894ab 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -22,6 +22,7 @@ #include "journal-internal.h" #include "journal-util.h" #include "json.h" +#include "locale-util.h" #include "log.h" #include "logs-show.h" #include "macro.h" @@ -39,6 +40,7 @@ #include "time-util.h" #include "utf8.h" #include "util.h" +#include "web-util.h" /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */ #define PRINT_LINE_THRESHOLD 3 @@ -47,21 +49,86 @@ #define JSON_THRESHOLD 4096U static int print_catalog(FILE *f, sd_journal *j) { - int r; _cleanup_free_ char *t = NULL, *z = NULL; + const char *newline, *prefix; + int r; + + assert(j); r = sd_journal_get_catalog(j, &t); + if (r == -ENOENT) + return 0; if (r < 0) - return r; + return log_error_errno(r, "Failed to find catalog entry: %m"); - z = strreplace(strstrip(t), "\n", "\n-- "); + if (is_locale_utf8()) + prefix = strjoina(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), special_glyph(SPECIAL_GLYPH_LIGHT_SHADE)); + else + prefix = "--"; + + if (colors_enabled()) + newline = strjoina(ANSI_NORMAL "\n" ANSI_GREY, prefix, ANSI_NORMAL " " ANSI_GREEN); + else + newline = strjoina("\n", prefix, " "); + + z = strreplace(strstrip(t), "\n", newline); if (!z) return log_oom(); - fputs("-- ", f); - fputs(z, f); - fputc('\n', f); + if (colors_enabled()) + fprintf(f, ANSI_GREY "%s" ANSI_NORMAL " " ANSI_GREEN, prefix); + else + fprintf(f, "%s ", prefix); + fputs(z, f); + + if (colors_enabled()) + fputs(ANSI_NORMAL "\n", f); + else + fputc('\n', f); + + return 1; +} + +static int url_from_catalog(sd_journal *j, char **ret) { + _cleanup_free_ char *t = NULL, *url = NULL; + const char *weblink; + int r; + + assert(j); + assert(ret); + + r = sd_journal_get_catalog(j, &t); + if (r == -ENOENT) + goto notfound; + if (r < 0) + return log_error_errno(r, "Failed to find catalog entry: %m"); + + weblink = startswith(t, "Documentation:"); + if (!weblink) { + weblink = strstr(t + 1, "\nDocumentation:"); + if (!weblink) + goto notfound; + + weblink += 15; + } + + /* Skip whitespace to value */ + weblink += strspn(weblink, " \t"); + + /* Cut out till next whitespace/newline */ + url = strndup(weblink, strcspn(weblink, WHITESPACE)); + if (!url) + return log_oom(); + + if (!documentation_url_is_valid(url)) + goto notfound; + + *ret = TAKE_PTR(url); + return 1; + +notfound: + *ret = NULL; return 0; } @@ -121,17 +188,14 @@ static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fi return 0; } -static int field_set_test(Set *fields, const char *name, size_t n) { - char *s = NULL; +static int field_set_test(const Set *fields, const char *name, size_t n) { + char *s; if (!fields) return 1; s = strndupa(name, n); - if (!s) - return log_oom(); - - return set_get(fields, s) ? 1 : 0; + return set_contains(fields, s); } static bool shall_print(const char *p, size_t l, OutputFlags flags) { @@ -369,15 +433,18 @@ static int output_short( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) { int r; const void *data; - size_t length; - size_t n = 0; - _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *config_file = NULL, *unit = NULL, *user_unit = NULL; - size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0, unit_len = 0, user_unit_len = 0; + size_t length, n = 0; + _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, + *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, + *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL; + size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, + realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0, + unit_len = 0, user_unit_len = 0, documentation_url_len = 0; int p = LOG_INFO; bool ellipsized = false, audit; const ParseFieldVec fields[] = { @@ -394,6 +461,7 @@ static int output_short( PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len), PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len), PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len), + PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len), }; size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0}; @@ -482,30 +550,64 @@ static int output_short( n += fake_pid_len + 2; } + fputs(": ", f); + + if (urlify_enabled()) { + _cleanup_free_ char *c = NULL; + + /* Insert a hyperlink to a documentation URL before the message. Note that we don't make the + * whole message a hyperlink, since otherwise the whole screen might end up being just + * hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the + * config file, see below) hence let's keep the documentation URL link separate. */ + + if (documentation_url && shall_print(documentation_url, documentation_url_len, flags)) { + c = strndup(documentation_url, documentation_url_len); + if (!c) + return log_oom(); + + if (!documentation_url_is_valid(c)) /* Eat up invalid links */ + c = mfree(c); + } + + if (!c) + (void) url_from_catalog(j, &c); /* Acquire from catalog if not embedded in log message itself */ + + if (c) { + _cleanup_free_ char *urlified = NULL; + + if (terminal_urlify(c, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK), &urlified) >= 0) { + fputs(urlified, f); + fputc(' ', f); + } + } + } + if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) { char bytes[FORMAT_BYTES_MAX]; - fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); + fprintf(f, "[%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); } else { - fputs(": ", f); /* URLify config_file string in message, if the message starts with it. * Skip URLification if the highlighted pattern overlaps. */ if (config_file && message_len >= config_file_len && memcmp(message, config_file, config_file_len) == 0 && - IN_SET(message[config_file_len], ':', ' ', '\0') && + (message_len == config_file_len || IN_SET(message[config_file_len], ':', ' ')) && (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) { _cleanup_free_ char *t = NULL, *urlified = NULL; t = strndup(config_file, config_file_len); if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) { - size_t shift = strlen(urlified) - config_file_len; + size_t urlified_len = strlen(urlified); + size_t shift = urlified_len - config_file_len; char *joined; - joined = strjoin(urlified, message + config_file_len); + joined = realloc(urlified, message_len + shift); if (joined) { + memcpy(joined + urlified_len, message + config_file_len, message_len - config_file_len); free_and_replace(message, joined); + TAKE_PTR(urlified); message_len += shift; if (highlight) { highlight_shifted[0] += shift; @@ -522,7 +624,7 @@ static int output_short( } if (flags & OUTPUT_CATALOG) - print_catalog(f, j); + (void) print_catalog(f, j); return ellipsized; } @@ -533,7 +635,7 @@ static int output_verbose( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) { const void *data; @@ -641,7 +743,7 @@ static int output_verbose( return r; if (flags & OUTPUT_CATALOG) - print_catalog(f, j); + (void) print_catalog(f, j); return 0; } @@ -652,7 +754,7 @@ static int output_export( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) { sd_id128_t boot_id; @@ -849,7 +951,7 @@ static int update_json_data( static int update_json_data_split( Hashmap *h, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const void *data, size_t size) { @@ -870,7 +972,7 @@ static int update_json_data_split( return 0; name = strndupa(data, eq - (const char*) data); - if (output_fields && !set_get(output_fields, name)) + if (output_fields && !set_contains(output_fields, name)) return 0; return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1); @@ -882,7 +984,7 @@ static int output_json( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) { char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)]; @@ -1016,58 +1118,84 @@ finish: return r; } +static int output_cat_field( + FILE *f, + sd_journal *j, + OutputFlags flags, + const char *field, + const size_t highlight[2]) { + + const char *highlight_on, *highlight_off; + const void *data; + size_t l, fl; + int r; + + if (FLAGS_SET(flags, OUTPUT_COLOR)) { + highlight_on = ANSI_HIGHLIGHT_RED; + highlight_off = ANSI_NORMAL; + } else + highlight_on = highlight_off = ""; + + r = sd_journal_get_data(j, field, &data, &l); + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } + if (r == -ENOENT) /* An entry without the requested field */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to get data: %m"); + + fl = strlen(field); + assert(l >= fl + 1); + assert(((char*) data)[fl] == '='); + + data = (const uint8_t*) data + fl + 1; + l -= fl + 1; + + if (highlight && FLAGS_SET(flags, OUTPUT_COLOR)) { + assert(highlight[0] <= highlight[1]); + assert(highlight[1] <= l); + + fwrite((const char*) data, 1, highlight[0], f); + fwrite(highlight_on, 1, strlen(highlight_on), f); + fwrite((const char*) data + highlight[0], 1, highlight[1] - highlight[0], f); + fwrite(highlight_off, 1, strlen(highlight_off), f); + fwrite((const char*) data + highlight[1], 1, l - highlight[1], f); + } else + fwrite((const char*) data, 1, l, f); + + fputc('\n', f); + return 0; +} + static int output_cat( FILE *f, sd_journal *j, OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) { - const void *data; - size_t l; + const char *field; + Iterator iterator; int r; - const char *highlight_on = "", *highlight_off = ""; assert(j); assert(f); - if (flags & OUTPUT_COLOR) { - highlight_on = ANSI_HIGHLIGHT_RED; - highlight_off = ANSI_NORMAL; + (void) sd_journal_set_data_threshold(j, 0); + + if (set_isempty(output_fields)) + return output_cat_field(f, j, flags, "MESSAGE", highlight); + + SET_FOREACH(field, output_fields, iterator) { + r = output_cat_field(f, j, flags, field, streq(field, "MESSAGE") ? highlight : NULL); + if (r < 0) + return r; } - sd_journal_set_data_threshold(j, 0); - - r = sd_journal_get_data(j, "MESSAGE", &data, &l); - if (r == -EBADMSG) { - log_debug_errno(r, "Skipping message we can't read: %m"); - return 0; - } - if (r < 0) { - /* An entry without MESSAGE=? */ - if (r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to get data: %m"); - } - - assert(l >= 8); - - if (highlight && (flags & OUTPUT_COLOR)) { - assert(highlight[0] <= highlight[1]); - assert(highlight[1] <= l - 8); - - fwrite((const char*) data + 8, 1, highlight[0], f); - fwrite(highlight_on, 1, strlen(highlight_on), f); - fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f); - fwrite(highlight_off, 1, strlen(highlight_off), f); - fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f); - } else - fwrite((const char*) data + 8, 1, l - 8, f); - fputc('\n', f); - return 0; } @@ -1077,7 +1205,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields, + const Set *output_fields, const size_t highlight[2]) = { [OUTPUT_SHORT] = output_short, @@ -1107,30 +1235,25 @@ int show_journal_entry( const size_t highlight[2], bool *ellipsized) { - int ret; - _cleanup_set_free_free_ Set *fields = NULL; + _cleanup_set_free_ Set *fields = NULL; + int r; + assert(mode >= 0); assert(mode < _OUTPUT_MODE_MAX); if (n_columns <= 0) n_columns = columns(); - if (output_fields) { - fields = set_new(&string_hash_ops); - if (!fields) - return log_oom(); + r = set_put_strdupv(&fields, output_fields); + if (r < 0) + return r; - ret = set_put_strdupv(fields, output_fields); - if (ret < 0) - return ret; - } + r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight); - ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight); - - if (ellipsized && ret > 0) + if (ellipsized && r > 0) *ellipsized = true; - return ret; + return r; } static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) { diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 7aee239e3..4a593b05f 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -191,6 +191,10 @@ LoopDevice* loop_device_unref(LoopDevice *d) { return NULL; if (d->fd >= 0) { + /* Implicitly sync the device, since otherwise in-flight blocks might not get written */ + if (fsync(d->fd) < 0) + log_debug_errno(errno, "Failed to sync loop block device, ignoring: %m"); + if (d->nr >= 0 && !d->relinquished) { if (ioctl(d->fd, LOOP_CLR_FD) < 0) log_debug_errno(errno, "Failed to clear loop device: %m"); @@ -216,7 +220,7 @@ LoopDevice* loop_device_unref(LoopDevice *d) { log_warning_errno(errno, "Failed to remove device %s: %m", strna(d->node)); break; } - usleep(50 * USEC_PER_MSEC); + (void) usleep(50 * USEC_PER_MSEC); } } diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index b45efcd1e..1b7cfb502 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) { if (r < 0) return r; - r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); + r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); if (r < 0) return r; diff --git a/src/shared/macvlan-util.c b/src/shared/macvlan-util.c new file mode 100644 index 000000000..926b4d42a --- /dev/null +++ b/src/shared/macvlan-util.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "conf-parser.h" +#include "macvlan-util.h" +#include "string-table.h" + +static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = { + [NETDEV_MACVLAN_MODE_PRIVATE] = "private", + [NETDEV_MACVLAN_MODE_VEPA] = "vepa", + [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge", + [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru", + [NETDEV_MACVLAN_MODE_SOURCE] = "source", +}; + +DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); diff --git a/src/shared/macvlan-util.h b/src/shared/macvlan-util.h new file mode 100644 index 000000000..7670bbf40 --- /dev/null +++ b/src/shared/macvlan-util.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +typedef enum MacVlanMode { + NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, + NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA, + NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE, + NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU, + NETDEV_MACVLAN_MODE_SOURCE = MACVLAN_MODE_SOURCE, + _NETDEV_MACVLAN_MODE_MAX, + _NETDEV_MACVLAN_MODE_INVALID = -1 +} MacVlanMode; + +const char *macvlan_mode_to_string(MacVlanMode d) _const_; +MacVlanMode macvlan_mode_from_string(const char *d) _pure_; diff --git a/src/shared/meson.build b/src/shared/meson.build index 0af8ed77c..0da733c3f 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -17,20 +17,38 @@ shared_sources = files(''' bitmap.c bitmap.h blkid-util.h + bond-util.c + bond-util.h boot-timestamps.c boot-timestamps.h bootspec.c bootspec.h bpf-program.c bpf-program.h + bridge-util.c + bridge-util.h + bus-get-properties.c + bus-get-properties.h + bus-locator.c + bus-locator.h + bus-log-control-api.c + bus-log-control-api.h + bus-map-properties.c + bus-map-properties.h + bus-message-util.c + bus-message-util.h + bus-object.c + bus-object.h + bus-polkit.c + bus-polkit.h + bus-print-properties.c + bus-print-properties.h bus-unit-procs.c bus-unit-procs.h bus-unit-util.c bus-unit-util.h bus-util.c bus-util.h - bus-polkit.c - bus-polkit.h bus-wait-for-jobs.c bus-wait-for-jobs.h bus-wait-for-units.c @@ -51,6 +69,8 @@ shared_sources = files(''' condition.h conf-parser.c conf-parser.h + coredump-util.c + coredump-util.h cpu-set-util.c cpu-set-util.h crypt-util.c @@ -89,6 +109,8 @@ shared_sources = files(''' fstab-util.h generator.c generator.h + geneve-util.c + geneve-util.h gpt.c gpt.h group-record-nss.c @@ -108,6 +130,8 @@ shared_sources = files(''' install-printf.h install.c install.h + ipvlan-util.c + ipvlan-util.h ip-protocol-list.c ip-protocol-list.h journal-importer.c @@ -139,6 +163,8 @@ shared_sources = files(''' machine-image.h machine-pool.c machine-pool.h + macvlan-util.c + macvlan-util.h main-func.h module-util.h mount-util.c @@ -158,8 +184,6 @@ shared_sources = files(''' output-mode.h pager.c pager.h - path-lookup.c - path-lookup.h pe-header.h pkcs11-util.c pkcs11-util.h @@ -178,6 +202,8 @@ shared_sources = files(''' securebits-util.h serialize.c serialize.h + service-util.c + service-util.h sleep-config.c sleep-config.h socket-netlink.c @@ -323,6 +349,7 @@ libshared_deps = [threads, librt, libseccomp, libselinux, + libzstd, libxz] libshared_sym_path = '@0@/libshared.sym'.format(meson.current_source_dir()) diff --git a/src/shared/module-util.c b/src/shared/module-util.c index 9173cf2ff..3e64d423c 100644 --- a/src/shared/module-util.c +++ b/src/shared/module-util.c @@ -51,7 +51,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) "Inserted module '%s'", kmod_module_get_name(mod)); else if (err == KMOD_PROBE_APPLY_BLACKLIST) log_full(verbose ? LOG_INFO : LOG_DEBUG, - "Module '%s' is blacklisted", kmod_module_get_name(mod)); + "Module '%s' is deny-listed", kmod_module_get_name(mod)); else { assert(err < 0); diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 32c533282..b3fac13f7 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -58,8 +58,8 @@ int umount_recursive(const char *prefix, int flags) { if (!path_startswith(path, prefix)) continue; - if (umount2(path, flags) < 0) { - r = log_debug_errno(errno, "Failed to umount %s: %m", path); + if (umount2(path, flags | UMOUNT_NOFOLLOW) < 0) { + log_debug_errno(errno, "Failed to umount %s, ignoring: %m", path); continue; } @@ -70,7 +70,6 @@ int umount_recursive(const char *prefix, int flags) { break; } - } while (again); return n; @@ -136,7 +135,7 @@ int bind_remount_recursive_with_mountinfo( const char *prefix, unsigned long new_flags, unsigned long flags_mask, - char **blacklist, + char **deny_list, FILE *proc_self_mountinfo) { _cleanup_set_free_free_ Set *done = NULL; @@ -151,11 +150,11 @@ int bind_remount_recursive_with_mountinfo( * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We - * do not have any effect on future submounts that might get propagated, they migt be writable. This includes + * do not have any effect on future submounts that might get propagated, they might be writable. This includes * future submounts that have been triggered via autofs. * - * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the - * remount operation. Note that we'll ignore the blacklist for the top-level path. */ + * If the "deny_list" parameter is specified it may contain a list of subtrees to exclude from the + * remount operation. Note that we'll ignore the deny list for the top-level path. */ simplified = strdup(prefix); if (!simplified) @@ -203,13 +202,13 @@ int bind_remount_recursive_with_mountinfo( if (!path_startswith(path, simplified)) continue; - /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount + /* Ignore this mount if it is deny-listed, but only if it isn't the top-level mount * we shall operate on. */ if (!path_equal(path, simplified)) { - bool blacklisted = false; + bool deny_listed = false; char **i; - STRV_FOREACH(i, blacklist) { + STRV_FOREACH(i, deny_list) { if (path_equal(*i, simplified)) continue; @@ -217,13 +216,13 @@ int bind_remount_recursive_with_mountinfo( continue; if (path_startswith(path, *i)) { - blacklisted = true; - log_debug("Not remounting %s blacklisted by %s, called for %s", + deny_listed = true; + log_debug("Not remounting %s deny-listed by %s, called for %s", path, *i, simplified); break; } } - if (blacklisted) + if (deny_listed) continue; } @@ -239,7 +238,7 @@ int bind_remount_recursive_with_mountinfo( } if (!set_contains(done, path)) { - r = set_put_strdup(todo, path); + r = set_put_strdup(&todo, path); if (r < 0) return r; } @@ -266,7 +265,7 @@ int bind_remount_recursive_with_mountinfo( log_debug("Made top-level directory %s a mount point.", prefix); - r = set_put_strdup(done, simplified); + r = set_put_strdup(&done, simplified); if (r < 0) return r; } @@ -314,7 +313,7 @@ int bind_remount_recursive( const char *prefix, unsigned long new_flags, unsigned long flags_mask, - char **blacklist) { + char **deny_list) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; int r; @@ -323,7 +322,7 @@ int bind_remount_recursive( if (r < 0) return r; - return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, blacklist, proc_self_mountinfo); + return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, deny_list, proc_self_mountinfo); } int bind_remount_one_with_mountinfo( @@ -397,71 +396,73 @@ int repeat_unmount(const char *path, int flags) { } } -int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest) { - /* This function maps a node type to a corresponding inaccessible file node. These nodes are created during - * early boot by PID 1. In some cases we lacked the privs to create the character and block devices (maybe - * because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a devices policy that excludes - * device nodes with major and minor of 0), but that's fine, in that case we use an AF_UNIX file node instead, - * which is not the same, but close enough for most uses. And most importantly, the kernel allows bind mounts - * from socket nodes to any non-directory file nodes, and that's the most important thing that matters. */ +int mode_to_inaccessible_node( + const char *runtime_dir, + mode_t mode, + char **ret) { + + /* This function maps a node type to a corresponding inaccessible file node. These nodes are created + * during early boot by PID 1. In some cases we lacked the privs to create the character and block + * devices (maybe because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a + * devices policy that excludes device nodes with major and minor of 0), but that's fine, in that + * case we use an AF_UNIX file node instead, which is not the same, but close enough for most + * uses. And most importantly, the kernel allows bind mounts from socket nodes to any non-directory + * file nodes, and that's the most important thing that matters. + * + * Note that the runtime directory argument shall be the top-level runtime directory, i.e. /run/ if + * we operate in system context and $XDG_RUNTIME_DIR if we operate in user context. */ + _cleanup_free_ char *d = NULL; const char *node = NULL; - char *tmp; + bool fallback = false; - assert(dest); + assert(ret); + + if (!runtime_dir) + runtime_dir = "/run"; switch(mode & S_IFMT) { case S_IFREG: - node = "/inaccessible/reg"; + node = "/systemd/inaccessible/reg"; break; case S_IFDIR: - node = "/inaccessible/dir"; + node = "/systemd/inaccessible/dir"; break; case S_IFCHR: - d = path_join(runtime_dir, "/inaccessible/chr"); - if (!d) - return log_oom(); - - if (access(d, F_OK) == 0) { - *dest = TAKE_PTR(d); - return 0; - } - - node = "/inaccessible/sock"; + node = "/systemd/inaccessible/chr"; + fallback = true; break; case S_IFBLK: - d = path_join(runtime_dir, "/inaccessible/blk"); - if (!d) - return log_oom(); - - if (access(d, F_OK) == 0) { - *dest = TAKE_PTR(d); - return 0; - } - - node = "/inaccessible/sock"; + node = "/systemd/inaccessible/blk"; + fallback = true; break; case S_IFIFO: - node = "/inaccessible/fifo"; + node = "/systemd/inaccessible/fifo"; break; case S_IFSOCK: - node = "/inaccessible/sock"; + node = "/systemd/inaccessible/sock"; break; } - if (!node) return -EINVAL; - tmp = path_join(runtime_dir, node); - if (!tmp) - return log_oom(); + d = path_join(runtime_dir, node); + if (!d) + return -ENOMEM; - *dest = tmp; + if (fallback && access(d, F_OK) < 0) { + free(d); + d = path_join(runtime_dir, "/systemd/inaccessible/sock"); + if (!d) + return -ENOMEM; + } + + *ret = TAKE_PTR(d); return 0; } diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index 06fddacf1..8fb597e7c 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -3,13 +3,39 @@ #include #include +#include +#include "errno-util.h" #include "macro.h" +/* 4MB for contents of regular files, 64k inodes for directories, symbolic links and device specials, using + * large storage array systems as a baseline */ +#define TMPFS_LIMITS_DEV ",size=4m,nr_inodes=64k" + +/* Very little, if any use expected */ +#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k" +#define TMPFS_LIMITS_SYS TMPFS_LIMITS_EMPTY_OR_ALMOST +#define TMPFS_LIMITS_SYS_FS_CGROUP TMPFS_LIMITS_EMPTY_OR_ALMOST + +/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of + * PID1 because 16MB of free space is required. */ +#define TMPFS_LIMITS_RUN ",size=20%,nr_inodes=800k" + +/* The limit used for various nested tmpfs mounts, in paricular for guests started by systemd-nspawn. + * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25% + * translates to 1M inodes. + * (On the host, /tmp is configured through a .mount unit file.) */ +#define NESTED_TMPFS_LIMITS ",size=10%,nr_inodes=400k" + +/* More space for volatile root and /var */ +#define TMPFS_LIMITS_VAR ",size=25%,nr_inodes=1m" +#define TMPFS_LIMITS_ROOTFS TMPFS_LIMITS_VAR +#define TMPFS_LIMITS_VOLATILE_STATE TMPFS_LIMITS_VAR + int repeat_unmount(const char *path, int flags); int umount_recursive(const char *target, int flags); -int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist); -int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo); +int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list); +int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list, FILE *proc_self_mountinfo); int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo); int mount_move_root(const char *path); @@ -33,3 +59,12 @@ int mount_option_mangle( char **ret_remaining_options); int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest); + +/* Useful for usage with _cleanup_(), unmounts, removes a directory and frees the pointer */ +static inline void umount_and_rmdir_and_free(char *p) { + PROTECT_ERRNO; + (void) umount_recursive(p, 0); + (void) rmdir(p); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free); diff --git a/src/shared/nscd-flush.c b/src/shared/nscd-flush.c index 33a06a010..10fdbaf32 100644 --- a/src/shared/nscd-flush.c +++ b/src/shared/nscd-flush.c @@ -134,7 +134,7 @@ int nscd_flush_cache(char **databases) { int r = 0; char **i; - /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s time-out, so that we + /* 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. */ end = usec_add(now(CLOCK_MONOTONIC), NSCD_FLUSH_CACHE_TIMEOUT_USEC); diff --git a/src/shared/offline-passwd.c b/src/shared/offline-passwd.c new file mode 100644 index 000000000..26a1b9c53 --- /dev/null +++ b/src/shared/offline-passwd.c @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "fd-util.h" +#include "fs-util.h" +#include "offline-passwd.h" +#include "path-util.h" +#include "user-util.h" + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free); + +static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + + fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p); + if (fd < 0) + return fd; + + FILE *f = fdopen(fd, "r"); + if (!f) + return -errno; + + TAKE_FD(fd); + + log_debug("Reading %s entries from %s...", basename(fname), p); + + *ret_file = f; + return 0; +} + +static int populate_uid_cache(const char *root, Hashmap **ret) { + _cleanup_(hashmap_freep) Hashmap *cache = NULL; + int r; + + cache = hashmap_new(&uid_gid_hash_ops); + if (!cache) + return -ENOMEM; + + /* The directory list is harcoded 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; + + r = open_passwd_file(root, fname, &f); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + struct passwd *pw; + while ((r = fgetpwent_sane(f, &pw)) > 0) { + _cleanup_free_ char *n = NULL; + + n = strdup(pw->pw_name); + if (!n) + return -ENOMEM; + + r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid)); + if (IN_SET(r, 0 -EEXIST)) + continue; + if (r < 0) + return r; + TAKE_PTR(n); + } + } + + *ret = TAKE_PTR(cache); + return 0; +} + +static int populate_gid_cache(const char *root, Hashmap **ret) { + _cleanup_(hashmap_freep) Hashmap *cache = NULL; + int r; + + cache = hashmap_new(&uid_gid_hash_ops); + if (!cache) + return -ENOMEM; + + const char *fname; + FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") { + _cleanup_fclose_ FILE *f = NULL; + + r = open_passwd_file(root, fname, &f); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + struct group *gr; + while ((r = fgetgrent_sane(f, &gr)) > 0) { + _cleanup_free_ char *n = NULL; + + n = strdup(gr->gr_name); + if (!n) + return -ENOMEM; + + r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid)); + if (IN_SET(r, 0, -EEXIST)) + continue; + if (r < 0) + return r; + TAKE_PTR(n); + } + } + + *ret = TAKE_PTR(cache); + return 0; +} + +int name_to_uid_offline( + const char *root, + const char *user, + uid_t *ret_uid, + Hashmap **cache) { + + void *found; + int r; + + assert(user); + assert(ret_uid); + assert(cache); + + if (!*cache) { + r = populate_uid_cache(root, cache); + if (r < 0) + return r; + } + + found = hashmap_get(*cache, user); + if (!found) + return -ESRCH; + + *ret_uid = PTR_TO_UID(found); + return 0; +} + +int name_to_gid_offline( + const char *root, + const char *group, + gid_t *ret_gid, + Hashmap **cache) { + + void *found; + int r; + + assert(group); + assert(ret_gid); + assert(cache); + + if (!*cache) { + r = populate_gid_cache(root, cache); + if (r < 0) + return r; + } + + found = hashmap_get(*cache, group); + if (!found) + return -ESRCH; + + *ret_gid = PTR_TO_GID(found); + return 0; +} diff --git a/src/shared/offline-passwd.h b/src/shared/offline-passwd.h new file mode 100644 index 000000000..90bdfc79b --- /dev/null +++ b/src/shared/offline-passwd.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "hashmap.h" + +int name_to_uid_offline(const char *root, const char *user, uid_t *ret_uid, Hashmap **cache); +int name_to_gid_offline(const char *root, const char *group, gid_t *ret_gid, Hashmap **cache); diff --git a/src/shared/os-util.c b/src/shared/os-util.c index b2af8535f..042e77c8c 100644 --- a/src/shared/os-util.c +++ b/src/shared/os-util.c @@ -3,6 +3,7 @@ #include "alloc-util.h" #include "env-file.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "os-util.h" @@ -76,10 +77,9 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) { if (r < 0) return r; - f = fdopen(fd, "r"); + f = take_fdopen(&fd, "r"); if (!f) return -errno; - fd = -1; *ret_file = f; @@ -117,3 +117,33 @@ int load_os_release_pairs(const char *root, char ***ret) { return load_env_file_pairs(f, p, 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); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(p, q, os_release_pairs) { + char *line; + + /* We strictly return only the four main ID fields and ignore the rest */ + if (!STR_IN_SET(*p, "ID", "VERSION_ID", "BUILD_ID", "VARIANT_ID")) + continue; + + ascii_strlower(*p); + line = strjoin(prefix, *p, "=", *q); + if (!line) + return -ENOMEM; + r = strv_consume(&os_release_pairs_prefixed, line); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(os_release_pairs_prefixed); + + return 0; +} diff --git a/src/shared/os-util.h b/src/shared/os-util.h index 27ec7ac8d..b54bb0916 100644 --- a/src/shared/os-util.h +++ b/src/shared/os-util.h @@ -10,3 +10,4 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file); int parse_os_release(const char *root, ...) _sentinel_; 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); diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 3fcd7630d..632964df4 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -151,6 +151,28 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) { return t; } +char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info) { + char *t; + + t = strndup((char*) token_info->manufacturerID, sizeof(token_info->manufacturerID)); + if (!t) + return NULL; + + strstrip(t); + return t; +} + +char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) { + char *t; + + t = strndup((char*) token_info->model, sizeof(token_info->model)); + if (!t) + return NULL; + + strstrip(t); + return t; +} + int pkcs11_token_login( CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, @@ -165,9 +187,8 @@ int pkcs11_token_login( _cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL; _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL; CK_TOKEN_INFO updated_token_info; - int uri_result; + int uri_result, r; CK_RV rv; - int r; assert(m); assert(token_info); @@ -190,7 +211,7 @@ int pkcs11_token_login( return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv)); - log_info("Successully logged into security token '%s' via protected authentication path.", token_label); + log_info("Successfully logged into security token '%s' via protected authentication path.", token_label); *ret_used_pin = NULL; return 0; } @@ -211,28 +232,8 @@ int pkcs11_token_login( for (unsigned tries = 0; tries < 3; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; - _cleanup_free_ char *text = NULL; char **i, *e; - if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY)) - r = asprintf(&text, - "Please enter correct PIN for security token '%s' in order to unlock %s (final try):", - token_label, friendly_name); - else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW)) - r = asprintf(&text, - "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:", - token_label, friendly_name); - else if (tries == 0) - r = asprintf(&text, - "Please enter PIN for security token '%s' in order to unlock %s:", - token_label, friendly_name); - else - r = asprintf(&text, - "Please enter PIN for security token '%s' in order to unlock %s (try #%u):", - token_label, friendly_name, tries+1); - if (r < 0) - return log_oom(); - e = getenv("PIN"); if (e) { passwords = strv_new(e); @@ -243,6 +244,27 @@ int pkcs11_token_login( if (unsetenv("PIN") < 0) return log_error_errno(errno, "Failed to unset $PIN: %m"); } else { + _cleanup_free_ char *text = NULL; + + if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY)) + r = asprintf(&text, + "Please enter correct PIN for security token '%s' in order to unlock %s (final try):", + token_label, friendly_name); + else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW)) + r = asprintf(&text, + "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:", + token_label, friendly_name); + else if (tries == 0) + r = asprintf(&text, + "Please enter PIN for security token '%s' in order to unlock %s:", + token_label, friendly_name); + else + r = asprintf(&text, + "Please enter PIN for security token '%s' in order to unlock %s (try #%u):", + token_label, friendly_name, tries+1); + if (r < 0) + return log_oom(); + /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */ r = ask_password_auto(text, icon_name, id, keyname, until, 0, &passwords); if (r < 0) @@ -702,7 +724,6 @@ static int token_process( assert(m); assert(slot_info); assert(token_info); - assert(search_uri); token_label = pkcs11_token_label(token_info); if (!token_label) @@ -740,7 +761,6 @@ static int slot_process( CK_RV rv; assert(m); - assert(search_uri); /* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the * caller might try other slots before giving up. */ @@ -786,7 +806,7 @@ static int slot_process( return -EAGAIN; } - if (!p11_kit_uri_match_token_info(search_uri, &token_info)) { + if (search_uri && !p11_kit_uri_match_token_info(search_uri, &token_info)) { log_debug("Found non-matching token with URI %s.", token_uri_string); return -EAGAIN; } @@ -820,7 +840,6 @@ static int module_process( int r; assert(m); - assert(search_uri); /* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module * should not have the effect that we don't try the others anymore. We indicate such per-module @@ -883,14 +902,14 @@ int pkcs11_find_token( _cleanup_(p11_kit_uri_freep) P11KitUri *search_uri = NULL; int r; - assert(pkcs11_uri); - /* Execute the specified callback for each matching token found. If nothing is found returns * -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */ - r = uri_from_string(pkcs11_uri, &search_uri); - if (r < 0) - return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri); + if (pkcs11_uri) { + r = uri_from_string(pkcs11_uri, &search_uri); + if (r < 0) + return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri); + } modules = p11_kit_modules_load_and_initialize(0); if (!modules) diff --git a/src/shared/pkcs11-util.h b/src/shared/pkcs11-util.h index 46791eb23..959e7c3e0 100644 --- a/src/shared/pkcs11-util.h +++ b/src/shared/pkcs11-util.h @@ -27,6 +27,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(CK_FUNCTION_LIST**, p11_kit_modules_finalize_and_rel CK_RV pkcs11_get_slot_list_malloc(CK_FUNCTION_LIST *m, CK_SLOT_ID **ret_slotids, CK_ULONG *ret_n_slotids); char *pkcs11_token_label(const CK_TOKEN_INFO *token_info); +char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info); +char *pkcs11_token_model(const CK_TOKEN_INFO *token_info); int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *keyname, usec_t until, char **ret_used_pin); diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index 5772918c3..69bae6883 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -18,7 +18,7 @@ #include "terminal-util.h" #include "util.h" -static bool urlify_enabled(void) { +bool urlify_enabled(void) { static int cached_urlify_enabled = -1; /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h index 12ab9acf5..b3057ae6b 100644 --- a/src/shared/pretty-print.h +++ b/src/shared/pretty-print.h @@ -5,6 +5,8 @@ void print_separator(void); int file_url_from_path(const char *path, char **ret); +bool urlify_enabled(void); + int terminal_urlify(const char *url, const char *text, char **ret); int terminal_urlify_path(const char *path, const char *text, char **ret); int terminal_urlify_man(const char *page, const char *section, char **ret); diff --git a/src/shared/reboot-util.c b/src/shared/reboot-util.c index 5d7629917..888f685ae 100644 --- a/src/shared/reboot-util.c +++ b/src/shared/reboot-util.c @@ -55,12 +55,12 @@ int read_reboot_parameter(char **parameter) { int reboot_with_parameter(RebootFlags flags) { int r; - /* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if REBOOT_DRY_RUN - * was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is set and the reboot with - * parameter doesn't work out a fallback to classic reboot() is attempted. If REBOOT_FALLBACK is not set, 0 is - * returned instead, which should be considered indication for the caller to fall back to reboot() on its own, - * or somehow else deal with this. If REBOOT_LOG is specified will log about what it is going to do, as well as - * all errors. */ + /* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if + * REBOOT_DRY_RUN was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is + * set and the reboot with parameter doesn't work out a fallback to classic reboot() is attempted. If + * REBOOT_FALLBACK is not set, 0 is returned instead, which should be considered indication for the + * caller to fall back to reboot() on its own, or somehow else deal with this. If REBOOT_LOG is + * specified will log about what it is going to do, as well as all errors. */ if (detect_container() == 0) { _cleanup_free_ char *parameter = NULL; @@ -71,7 +71,6 @@ int reboot_with_parameter(RebootFlags flags) { "Failed to read reboot parameter file, ignoring: %m"); if (!isempty(parameter)) { - log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG, "Rebooting with argument '%s'.", parameter); diff --git a/src/shared/resize-fs.c b/src/shared/resize-fs.c index 0abd289fe..24a17a2ff 100644 --- a/src/shared/resize-fs.c +++ b/src/shared/resize-fs.c @@ -41,7 +41,6 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) { *ret_size = u * sfs.f_bsize; } else if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) { - _unused_ int r; struct btrfs_ioctl_vol_args args = {}; /* 256M is the minimize size enforced by the btrfs kernel code when resizing (which is @@ -54,8 +53,7 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) { sz -= sz % sfs.f_bsize; - r = snprintf(args.name, sizeof(args.name), "%" PRIu64, sz); - assert((size_t) r < sizeof(args.name)); + xsprintf(args.name, "%" PRIu64, sz); if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) return -errno; diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h index acf1f3dad..0524f1a74 100644 --- a/src/shared/resolve-util.h +++ b/src/shared/resolve-util.h @@ -81,3 +81,12 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa); const char* dns_cache_mode_to_string(DnsCacheMode p) _const_; DnsCacheMode dns_cache_mode_from_string(const char *s) _pure_; + +/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */ +#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf" + +/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */ +#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf" + +/* A static resolv.conf file containing no domains, but only our own DNS server address */ +#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf" diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 320b1767c..a8dd069a7 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -24,7 +24,7 @@ const uint32_t seccomp_local_archs[] = { - /* Note: always list the native arch we are compiled as last, so that users can blacklist seccomp(), but our own calls to it still succeed */ + /* Note: always list the native arch we are compiled as last, so that users can deny-list seccomp(), but our own calls to it still succeed */ #if defined(__x86_64__) && defined(__ILP32__) SCMP_ARCH_X86, @@ -1112,7 +1112,7 @@ int seccomp_parse_syscall_filter( /* If we previously wanted to forbid a syscall and now * we want to allow it, then remove it from the list. */ - if (!(flags & SECCOMP_PARSE_INVERT) == !!(flags & SECCOMP_PARSE_WHITELIST)) { + if (!(flags & SECCOMP_PARSE_INVERT) == !!(flags & SECCOMP_PARSE_ALLOW_LIST)) { r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); if (r < 0) switch (r) { @@ -1315,7 +1315,7 @@ int seccomp_protect_syslog(void) { return 0; } -int seccomp_restrict_address_families(Set *address_families, bool whitelist) { +int seccomp_restrict_address_families(Set *address_families, bool allow_list) { uint32_t arch; int r; @@ -1362,13 +1362,13 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { if (r < 0) return r; - if (whitelist) { + if (allow_list) { int af, first = 0, last = 0; void *afp; - /* If this is a whitelist, we first block the address families that are out of range and then - * everything that is not in the set. First, we find the lowest and highest address family in - * the set. */ + /* If this is an allow list, we first block the address families that are out of + * range and then everything that is not in the set. First, we find the lowest and + * highest address family in the set. */ SET_FOREACH(afp, address_families, i) { af = PTR_TO_INT(afp); @@ -1448,9 +1448,8 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { } else { void *af; - /* If this is a blacklist, then generate one rule for - * each address family that are then combined in OR - * checks. */ + /* If this is a deny list, then generate one rule for each address family that are + * then combined in OR checks. */ SET_FOREACH(af, address_families, i) { @@ -1506,11 +1505,11 @@ int seccomp_restrict_realtime(void) { return r; /* Go through all policies with lower values than that, and block them -- unless they appear in the - * whitelist. */ + * allow list. */ for (p = 0; p < max_policy; p++) { bool good = false; - /* Check if this is in the whitelist. */ + /* Check if this is in the allow list. */ for (i = 0; i < ELEMENTSOF(permitted_policies); i++) if (permitted_policies[i] == p) { good = true; @@ -1533,8 +1532,8 @@ int seccomp_restrict_realtime(void) { } } - /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are - * unsigned here, hence no need no check for < 0 values. */ + /* Deny-list all other policies, i.e. the ones with higher values. Note that all comparisons + * are unsigned here, hence no need no check for < 0 values. */ r = seccomp_rule_add_exact( seccomp, SCMP_ACT_ERRNO(EPERM), @@ -1742,17 +1741,13 @@ int seccomp_restrict_archs(Set *archs) { return 0; } -int parse_syscall_archs(char **l, Set **archs) { - _cleanup_set_free_ Set *_archs = NULL; +int parse_syscall_archs(char **l, Set **ret_archs) { + _cleanup_set_free_ Set *archs = NULL; char **s; int r; assert(l); - assert(archs); - - r = set_ensure_allocated(&_archs, NULL); - if (r < 0) - return r; + assert(ret_archs); STRV_FOREACH(s, l) { uint32_t a; @@ -1761,13 +1756,12 @@ int parse_syscall_archs(char **l, Set **archs) { if (r < 0) return -EINVAL; - r = set_put(_archs, UINT32_TO_PTR(a + 1)); + r = set_ensure_put(&archs, NULL, UINT32_TO_PTR(a + 1)); if (r < 0) return -ENOMEM; } - *archs = TAKE_PTR(_archs); - + *ret_archs = TAKE_PTR(archs); return 0; } @@ -2002,6 +1996,22 @@ static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) { else any = true; +#if defined(__SNR_openat2) + /* The new openat2() system call can't be filtered sensibly, since it moves the flags parameter into + * an indirect structure. Let's block it entirely for now. That should be a reasonably OK thing to do + * for now, since openat2() is very new and code generally needs fallback logic anyway to be + * compatible with kernels that are not absolutely recent. */ + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(openat2), + 0); + if (r < 0) + log_debug_errno(r, "Failed to add filter for openat2: %m"); + else + any = true; +#endif + r = seccomp_rule_add_exact( seccomp, SCMP_ACT_ERRNO(EPERM), diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 0b48e74a8..ef970434c 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -66,7 +66,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u typedef enum SeccompParseFlags { SECCOMP_PARSE_INVERT = 1 << 0, - SECCOMP_PARSE_WHITELIST = 1 << 1, + SECCOMP_PARSE_ALLOW_LIST = 1 << 1, SECCOMP_PARSE_LOG = 1 << 2, SECCOMP_PARSE_PERMISSIVE = 1 << 3, } SeccompParseFlags; @@ -83,7 +83,7 @@ int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); int seccomp_protect_sysctl(void); int seccomp_protect_syslog(void); -int seccomp_restrict_address_families(Set *address_families, bool whitelist); +int seccomp_restrict_address_families(Set *address_families, bool allow_list); int seccomp_restrict_realtime(void); int seccomp_memory_deny_write_execute(void); int seccomp_lock_personality(unsigned long personality); @@ -105,6 +105,6 @@ extern const uint32_t seccomp_local_archs[]; DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release); -int parse_syscall_archs(char **l, Set **archs); +int parse_syscall_archs(char **l, Set **ret_archs); uint32_t scmp_act_kill_process(void); diff --git a/src/shared/service-util.c b/src/shared/service-util.c new file mode 100644 index 000000000..c9b684fc0 --- /dev/null +++ b/src/shared/service-util.c @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include + +#include "alloc-util.h" +#include "pretty-print.h" +#include "service-util.h" +#include "terminal-util.h" +#include "util.h" + +static int help(const char *program_path, const char *service, const char *description, bool bus_introspect) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man(service, "8", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...]\n\n" + "%s%s%s\n\n" + "This program takes no positional arguments.\n\n" + "%sOptions%s:\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --bus-introspect=PATH Write D-Bus XML introspection data\n" + "\nSee the %s for details.\n" + , program_path + , ansi_highlight(), description, ansi_normal() + , ansi_underline(), ansi_normal() + , link + ); + + return 0; /* No further action */ +} + +int service_parse_argv( + const char *service, + const char *description, + const BusObjectImplementation* const* bus_objects, + int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_BUS_INTROSPECT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + + case 'h': + return help(argv[0], service, description, bus_objects); + + case ARG_VERSION: + return version(); + + case ARG_BUS_INTROSPECT: + return bus_introspect_implementations( + stdout, + optarg, + bus_objects); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unknown option code."); + } + + if (optind < argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This program takes no arguments."); + + return 1; /* Further action */ +} diff --git a/src/shared/service-util.h b/src/shared/service-util.h new file mode 100644 index 000000000..928c5961e --- /dev/null +++ b/src/shared/service-util.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "bus-object.h" + +int service_parse_argv( + const char *service, + const char *description, + const BusObjectImplementation* const* bus_objects, + int argc, char *argv[]); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index f8e23f953..0dccc8f97 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -59,10 +59,14 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) { {} }; - (void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", - CONF_PATHS_NULSTR("systemd/sleep.conf.d"), - "Sleep\0", config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + (void) config_parse_many_nulstr( + PKGSYSCONFDIR "/sleep.conf", + CONF_PATHS_NULSTR("systemd/sleep.conf.d"), + "Sleep\0", + config_item_table_lookup, items, + CONFIG_PARSE_WARN, + NULL, + NULL); /* use default values unless set */ sc->allow_suspend = allow_suspend != 0; @@ -121,10 +125,16 @@ int can_sleep_state(char **types) { k = strlen(*type); FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) - if (l == k && memcmp(word, *type, l) == 0) + if (l == k && memcmp(word, *type, l) == 0) { + log_debug("Sleep mode \"%s\" is supported by the kernel.", *type); return true; + } } + if (DEBUG_LOGGING) { + _cleanup_free_ char *t = strv_join(types, "/"); + log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t)); + } return false; } @@ -213,7 +223,7 @@ static int swap_device_to_device_id(const SwapEntry *swap, dev_t *ret_dev) { } /* - * Attempt to calculate the swap file offset on supported filesystems. On unsuported + * Attempt to calculate the swap file offset on supported filesystems. On unsupported * filesystems, a debug message is logged and ret_offset is set to UINT64_MAX. */ static int calculate_swap_file_offset(const SwapEntry *swap, uint64_t *ret_offset) { diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 5177137b9..d72a70503 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -243,8 +243,9 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) { assert(a); assert(s); - zero(*a); - a->type = SOCK_RAW; + *a = (SocketAddress) { + .type = SOCK_RAW, + }; r = extract_first_word(&s, &word, NULL, 0); if (r < 0) @@ -326,38 +327,197 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) { return fd; } -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) { - _cleanup_free_ char *buf = NULL; - const char *suffix; - int r, ifindex = 0; +int in_addr_port_ifindex_name_from_string_auto( + const char *s, + int *ret_family, + union in_addr_union *ret_address, + uint16_t *ret_port, + int *ret_ifindex, + char **ret_server_name) { + + _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL; + int family, ifindex = 0, r; + union in_addr_union a; + uint16_t port = 0; + const char *m; assert(s); - assert(family); - assert(ret_addr); - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ + /* This accepts the following: + * 192.168.0.1:53#example.com + * [2001:4860:4860::8888]:53%eth0#example.com */ + + /* if ret_port is NULL, then strings with port cannot be specified. + * Also, if ret_server_name is NULL, then server_name cannot be specified. */ + + m = strchr(s, '#'); + if (m) { + if (!ret_server_name) + return -EINVAL; + + if (isempty(m + 1)) + return -EINVAL; + + name = strdup(m + 1); + if (!name) + return -ENOMEM; + + s = buf1 = strndup(s, m - s); + if (!buf1) + return -ENOMEM; + } + + m = strchr(s, '%'); + if (m) { + if (isempty(m + 1)) + return -EINVAL; - suffix = strchr(s, '%'); - if (suffix) { if (ret_ifindex) { /* If we shall return the interface index, try to parse it */ - ifindex = resolve_interface(NULL, suffix + 1); + ifindex = resolve_interface(NULL, m + 1); if (ifindex < 0) return ifindex; } - s = buf = strndup(s, suffix - s); - if (!buf) + s = buf2 = strndup(s, m - s); + if (!buf2) return -ENOMEM; } - r = in_addr_from_string_auto(s, family, ret_addr); - if (r < 0) - return r; + m = strrchr(s, ':'); + if (m) { + if (*s == '[') { + _cleanup_free_ char *ip_str = NULL; + if (!ret_port) + return -EINVAL; + + if (*(m - 1) != ']') + return -EINVAL; + + family = AF_INET6; + + r = parse_ip_port(m + 1, &port); + if (r < 0) + return r; + + ip_str = strndup(s + 1, m - s - 2); + if (!ip_str) + return -ENOMEM; + + r = in_addr_from_string(family, ip_str, &a); + if (r < 0) + return r; + } else { + /* First try to parse the string as IPv6 address without port number */ + r = in_addr_from_string(AF_INET6, s, &a); + if (r < 0) { + /* Then the input should be IPv4 address with port number */ + _cleanup_free_ char *ip_str = NULL; + + if (!ret_port) + return -EINVAL; + + family = AF_INET; + + ip_str = strndup(s, m - s); + if (!ip_str) + return -ENOMEM; + + r = in_addr_from_string(family, ip_str, &a); + if (r < 0) + return r; + + r = parse_ip_port(m + 1, &port); + if (r < 0) + return r; + } else + family = AF_INET6; + } + } else { + family = AF_INET; + r = in_addr_from_string(family, s, &a); + if (r < 0) + return r; + } + + if (ret_family) + *ret_family = family; + if (ret_address) + *ret_address = a; + if (ret_port) + *ret_port = port; if (ret_ifindex) *ret_ifindex = ifindex; + if (ret_server_name) + *ret_server_name = TAKE_PTR(name); return r; } + +struct in_addr_full *in_addr_full_free(struct in_addr_full *a) { + if (!a) + return NULL; + + free(a->server_name); + free(a->cached_server_string); + return mfree(a); +} + +int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret) { + _cleanup_free_ char *name = NULL; + struct in_addr_full *x; + + assert(ret); + + if (!isempty(server_name)) { + name = strdup(server_name); + if (!name) + return -ENOMEM; + } + + x = new(struct in_addr_full, 1); + if (!x) + return -ENOMEM; + + *x = (struct in_addr_full) { + .family = family, + .address = *a, + .port = port, + .ifindex = ifindex, + .server_name = TAKE_PTR(name), + }; + + *ret = x; + return 0; +} + +int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) { + _cleanup_free_ char *server_name = NULL; + int family, ifindex, r; + union in_addr_union a; + uint16_t port; + + assert(s); + + r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name); + if (r < 0) + return r; + + return in_addr_full_new(family, &a, port, ifindex, server_name, ret); +} + +const char *in_addr_full_to_string(struct in_addr_full *a) { + assert(a); + + if (!a->cached_server_string) + (void) in_addr_port_ifindex_name_to_string( + a->family, + &a->address, + a->port, + a->ifindex, + a->server_name, + &a->cached_server_string); + + return a->cached_server_string; +} diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h index fa58409d6..9517f6dd6 100644 --- a/src/shared/socket-netlink.h +++ b/src/shared/socket-netlink.h @@ -20,4 +20,31 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s); bool socket_address_is(const SocketAddress *a, const char *s, int type); bool socket_address_is_netlink(const SocketAddress *a, const char *s); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); +int in_addr_port_ifindex_name_from_string_auto( + const char *s, + int *ret_family, + union in_addr_union *ret_address, + uint16_t *ret_port, + int *ret_ifindex, + char **ret_server_name); +static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) { + return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name); +} +static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { + return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL); +} + +struct in_addr_full { + int family; + union in_addr_union address; + uint16_t port; + int ifindex; + char *server_name; + char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */ +}; + +struct in_addr_full *in_addr_full_free(struct in_addr_full *a); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free); +int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret); +int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret); +const char *in_addr_full_to_string(struct in_addr_full *a); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index b036ff67d..112cf6f8f 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -9,10 +9,12 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "architecture.h" #include "format-util.h" #include "fs-util.h" #include "hostname-util.h" #include "macro.h" +#include "os-util.h" #include "specifier.h" #include "string-util.h" #include "strv.h" @@ -158,6 +160,17 @@ int specifier_host_name(char specifier, const void *data, const void *userdata, return 0; } +int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret) { + char *n; + + n = gethostname_short_malloc(); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) { struct utsname uts; char *n; @@ -175,6 +188,52 @@ int specifier_kernel_release(char specifier, const void *data, const void *userd return 0; } +int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret) { + char *t; + + t = strdup(architecture_to_string(uname_architecture())); + if (!t) + return -ENOMEM; + + *ret = t; + return 0; +} + +static int specifier_os_release_common(const char *field, char **ret) { + char *t = NULL; + int r; + + r = parse_os_release(NULL, field, &t, NULL); + 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; + } + + *ret = t; + return 0; +} + +int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret) { + return specifier_os_release_common("ID", ret); +} + +int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret) { + return specifier_os_release_common("VERSION_ID", ret); +} + +int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret) { + return specifier_os_release_common("BUILD_ID", ret); +} + +int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret) { + return specifier_os_release_common("VARIANT_ID", ret); +} + int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) { char *t; diff --git a/src/shared/specifier.h b/src/shared/specifier.h index d0221ef71..50c6cbd6a 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -18,7 +18,13 @@ int specifier_string(char specifier, const void *data, const void *userdata, cha int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret); int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret); int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret); +int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret); int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret); +int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret); +int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret); +int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret); +int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret); +int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret); int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret); int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret); diff --git a/src/shared/sysctl-util.h b/src/shared/sysctl-util.h index dce015151..316cfcccf 100644 --- a/src/shared/sysctl-util.h +++ b/src/shared/sysctl-util.h @@ -11,7 +11,7 @@ char *sysctl_normalize(char *s); int sysctl_read(const char *property, char **value); int sysctl_write(const char *property, const char *value); -int sysctl_writef(const char *propery, const char *format, ...) _printf_(2, 3); +int sysctl_writef(const char *property, const char *format, ...) _printf_(2, 3); int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret); int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value); diff --git a/src/shared/tests.c b/src/shared/tests.c index 96b5b805a..ff662ecfe 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -21,6 +21,7 @@ #include "env-util.h" #include "fs-util.h" #include "log.h" +#include "namespace-util.h" #include "path-util.h" #include "random-util.h" #include "strv.h" @@ -58,21 +59,25 @@ static void load_testdata_env(void) { setenv(*k, *v, 0); } -const char* get_testdata_dir(void) { - const char *env; +int get_testdata_dir(const char *suffix, char **ret) { + const char *dir; + char *p; load_testdata_env(); /* if the env var is set, use that */ - env = getenv("SYSTEMD_TEST_DATA"); - if (!env) - env = SYSTEMD_TEST_DATA; - if (access(env, F_OK) < 0) { - fprintf(stderr, "ERROR: $SYSTEMD_TEST_DATA directory [%s] does not exist\n", env); - exit(EXIT_FAILURE); - } + dir = getenv("SYSTEMD_TEST_DATA"); + if (!dir) + dir = SYSTEMD_TEST_DATA; + if (access(dir, F_OK) < 0) + return log_error_errno(errno, "ERROR: $SYSTEMD_TEST_DATA directory [%s] not accessible: %m", dir); - return env; + p = path_join(dir, suffix); + if (!p) + return log_oom(); + + *ret = p; + return 0; } const char* get_catalog_dir(void) { @@ -133,10 +138,7 @@ bool have_namespaces(void) { if (pid == 0) { /* child */ - if (unshare(CLONE_NEWNS) < 0) - _exit(EXIT_FAILURE); - - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) + if (detach_mount_namespace() < 0) _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); diff --git a/src/shared/tests.h b/src/shared/tests.h index 5a6fd53f3..6817ef486 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -20,7 +20,7 @@ static inline bool manager_errno_skip_test(int r) { char* setup_fake_runtime_dir(void); int enter_cgroup_subroot(char **ret_cgroup); -const char* get_testdata_dir(void); +int get_testdata_dir(const char *suffix, char **ret); const char* get_catalog_dir(void); bool slow_tests_enabled(void); void test_setup_logging(int level); diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index ea96f5b49..419c73318 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -7,6 +7,7 @@ #include "env-file.h" #include "log.h" #include "parse-util.h" +#include "signal-util.h" #include "string-table.h" #include "string-util.h" #include "udev-util.h" @@ -23,9 +24,10 @@ int udev_parse_config_full( unsigned *ret_children_max, usec_t *ret_exec_delay_usec, usec_t *ret_event_timeout_usec, - ResolveNameTiming *ret_resolve_name_timing) { + ResolveNameTiming *ret_resolve_name_timing, + int *ret_timeout_signal) { - _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL; + _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL, *timeout_signal = NULL; int r; r = parse_env_file(NULL, "/etc/udev/udev.conf", @@ -33,7 +35,8 @@ int udev_parse_config_full( "children_max", &children_max, "exec_delay", &exec_delay, "event_timeout", &event_timeout, - "resolve_names", &resolve_names); + "resolve_names", &resolve_names, + "timeout_signal", &timeout_signal); if (r == -ENOENT) return 0; if (r < 0) @@ -65,21 +68,21 @@ int udev_parse_config_full( r = safe_atou(children_max, ret_children_max); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse children_max=%s, ignoring: %m", children_max); + "failed to parse children_max=%s, ignoring: %m", children_max); } if (ret_exec_delay_usec && exec_delay) { r = parse_sec(exec_delay, ret_exec_delay_usec); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse exec_delay=%s, ignoring: %m", exec_delay); + "failed to parse exec_delay=%s, ignoring: %m", exec_delay); } if (ret_event_timeout_usec && event_timeout) { r = parse_sec(event_timeout, ret_event_timeout_usec); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse event_timeout=%s, ignoring: %m", event_timeout); + "failed to parse event_timeout=%s, ignoring: %m", event_timeout); } if (ret_resolve_name_timing && resolve_names) { @@ -88,11 +91,20 @@ int udev_parse_config_full( t = resolve_name_timing_from_string(resolve_names); if (t < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse resolve_names=%s, ignoring.", resolve_names); + "failed to parse resolve_names=%s, ignoring.", resolve_names); else *ret_resolve_name_timing = t; } + if (ret_timeout_signal && timeout_signal) { + r = signal_from_string(timeout_signal); + if (r < 0) + log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, + "failed to parse timeout_signal=%s, ignoring: %m", timeout_signal); + else + *ret_timeout_signal = r; + } + return 0; } diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index f62c4c56e..c35d29344 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -21,10 +21,11 @@ int udev_parse_config_full( unsigned *ret_children_max, usec_t *ret_exec_delay_usec, usec_t *ret_event_timeout_usec, - ResolveNameTiming *ret_resolve_name_timing); + ResolveNameTiming *ret_resolve_name_timing, + int *ret_timeout_signal); static inline int udev_parse_config(void) { - return udev_parse_config_full(NULL, NULL, NULL, NULL); + return udev_parse_config_full(NULL, NULL, NULL, NULL, NULL); } int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret); diff --git a/src/shared/unit-file.c b/src/shared/unit-file.c index 7b64bbf7f..ed4affd66 100644 --- a/src/shared/unit-file.c +++ b/src/shared/unit-file.c @@ -199,7 +199,7 @@ static bool lookup_paths_mtime_exclude(const LookupPaths *lp, const char *path) streq_ptr(path, lp->runtime_control); } -static bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) { +bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) { char **dir; STRV_FOREACH(dir, (char**) lp->search_path) { @@ -229,9 +229,9 @@ static bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) { int unit_file_build_name_map( const LookupPaths *lp, usec_t *cache_mtime, - Hashmap **ret_unit_ids_map, - Hashmap **ret_unit_names_map, - Set **ret_path_cache) { + Hashmap **unit_ids_map, + Hashmap **unit_names_map, + Set **path_cache) { /* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name → * all aliases (i.e. the entry for a given key is a a list of all names which point to this key). The @@ -239,7 +239,8 @@ int unit_file_build_name_map( * have a key, but it is not present in the value for itself, there was an alias pointing to it, but * the unit itself is not loadable. * - * At the same, build a cache of paths where to find units. + * At the same, build a cache of paths where to find units. The non-const parameters are for input + * and output. Existing contents will be freed before the new contents are stored. */ _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL; @@ -253,8 +254,8 @@ int unit_file_build_name_map( if (cache_mtime && *cache_mtime > 0 && lookup_paths_mtime_good(lp, *cache_mtime)) return 0; - if (ret_path_cache) { - paths = set_new(&path_hash_ops); + if (path_cache) { + paths = set_new(&path_hash_ops_free); if (!paths) return log_oom(); } @@ -296,7 +297,7 @@ int unit_file_build_name_map( if (!filename) return log_oom(); - if (ret_path_cache) { + if (paths) { r = set_consume(paths, filename); if (r < 0) return log_oom(); @@ -418,10 +419,11 @@ int unit_file_build_name_map( if (cache_mtime) *cache_mtime = mtime; - *ret_unit_ids_map = TAKE_PTR(ids); - *ret_unit_names_map = TAKE_PTR(names); - if (ret_path_cache) - *ret_path_cache = TAKE_PTR(paths); + + hashmap_free_and_replace(*unit_ids_map, ids); + hashmap_free_and_replace(*unit_names_map, names); + if (path_cache) + set_free_and_replace(*path_cache, paths); return 1; } @@ -463,7 +465,7 @@ int unit_file_find_fragment( /* The unit always has its own name if it's not a template. */ if (IN_SET(name_type, UNIT_NAME_PLAIN, UNIT_NAME_INSTANCE)) { - r = set_put_strdup(names, unit_name); + r = set_put_strdup(&names, unit_name); if (r < 0) return r; } @@ -493,7 +495,7 @@ int unit_file_find_fragment( if (!streq(unit_name, *t)) log_debug("%s: %s has alias %s", __func__, unit_name, *t); - r = set_put_strdup(names, *t); + r = set_put_strdup(&names, *t); } if (r < 0) return r; diff --git a/src/shared/unit-file.h b/src/shared/unit-file.h index a44ba5b05..d6d041d71 100644 --- a/src/shared/unit-file.h +++ b/src/shared/unit-file.h @@ -16,6 +16,7 @@ enum UnitFileState { UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME, + UNIT_FILE_ALIAS, UNIT_FILE_MASKED, UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_STATIC, @@ -42,6 +43,7 @@ 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); +bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime); int unit_file_build_name_map( const LookupPaths *lp, usec_t *ret_time, diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c index 0ff6d1711..f265a2af9 100644 --- a/src/shared/user-record-nss.c +++ b/src/shared/user-record-nss.c @@ -161,12 +161,16 @@ int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char ** } } -int nss_user_record_by_name(const char *name, UserRecord **ret) { +int nss_user_record_by_name( + const char *name, + bool with_shadow, + UserRecord **ret) { + _cleanup_free_ char *buf = NULL, *sbuf = NULL; struct passwd pwd, *result; bool incomplete = false; size_t buflen = 4096; - struct spwd spwd; + struct spwd spwd, *sresult = NULL; int r; assert(name); @@ -197,13 +201,17 @@ int nss_user_record_by_name(const char *name, UserRecord **ret) { buf = mfree(buf); } - r = nss_spwd_for_passwd(result, &spwd, &sbuf); - if (r < 0) { - log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name); - incomplete = ERRNO_IS_PRIVILEGE(r); - } + if (with_shadow) { + r = nss_spwd_for_passwd(result, &spwd, &sbuf); + if (r < 0) { + log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name); + incomplete = ERRNO_IS_PRIVILEGE(r); + } else + sresult = &spwd; + } else + incomplete = true; - r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret); + r = nss_passwd_to_user_record(result, sresult, ret); if (r < 0) return r; @@ -211,12 +219,16 @@ int nss_user_record_by_name(const char *name, UserRecord **ret) { return 0; } -int nss_user_record_by_uid(uid_t uid, UserRecord **ret) { +int nss_user_record_by_uid( + uid_t uid, + bool with_shadow, + UserRecord **ret) { + _cleanup_free_ char *buf = NULL, *sbuf = NULL; struct passwd pwd, *result; bool incomplete = false; size_t buflen = 4096; - struct spwd spwd; + struct spwd spwd, *sresult = NULL; int r; assert(ret); @@ -245,13 +257,17 @@ int nss_user_record_by_uid(uid_t uid, UserRecord **ret) { buf = mfree(buf); } - r = nss_spwd_for_passwd(result, &spwd, &sbuf); - if (r < 0) { - log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid); - incomplete = ERRNO_IS_PRIVILEGE(r); - } + if (with_shadow) { + r = nss_spwd_for_passwd(result, &spwd, &sbuf); + if (r < 0) { + log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid); + incomplete = ERRNO_IS_PRIVILEGE(r); + } else + sresult = &spwd; + } else + incomplete = true; - r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret); + r = nss_passwd_to_user_record(result, sresult, ret); if (r < 0) return r; diff --git a/src/shared/user-record-nss.h b/src/shared/user-record-nss.h index d5fb23ad2..0eb78d5b5 100644 --- a/src/shared/user-record-nss.h +++ b/src/shared/user-record-nss.h @@ -11,5 +11,5 @@ int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret); int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer); -int nss_user_record_by_name(const char *name, UserRecord **ret); -int nss_user_record_by_uid(uid_t uid, UserRecord **ret); +int nss_user_record_by_name(const char *name, bool with_shadow, UserRecord **ret); +int nss_user_record_by_uid(uid_t uid, bool with_shadow, UserRecord **ret); diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index e5eff5084..84ededd86 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -279,7 +279,7 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr)); if (storage == USER_LUKS) { - printf("LUKS Discard: %s\n", yes_no(user_record_luks_discard(hr))); + printf("LUKS Discard: online=%s offline=%s\n", yes_no(user_record_luks_discard(hr)), yes_no(user_record_luks_offline_discard(hr))); if (!sd_id128_is_null(hr->luks_uuid)) printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid)); @@ -340,12 +340,48 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (hr->disk_usage != UINT64_MAX) { char buf[FORMAT_BYTES_MAX]; - printf(" Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage)); + + if (hr->disk_size != UINT64_MAX) { + unsigned permille; + + permille = (unsigned) DIV_ROUND_UP(hr->disk_usage * 1000U, hr->disk_size); /* Round up! */ + printf(" Disk Usage: %s (= %u.%01u%%)\n", + format_bytes(buf, sizeof(buf), hr->disk_usage), + permille / 10, permille % 10); + } else + printf(" Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage)); } if (hr->disk_free != UINT64_MAX) { char buf[FORMAT_BYTES_MAX]; - printf(" Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free)); + + if (hr->disk_size != UINT64_MAX) { + const char *color_on, *color_off; + unsigned permille; + + permille = (unsigned) ((hr->disk_free * 1000U) / hr->disk_size); /* Round down! */ + + /* Color the output red or yellow if we are below 10% resp. 25% free. Because 10% and + * 25% can be a lot of space still, let's additionally make some absolute + * restrictions: 1G and 2G */ + if (permille <= 100U && + hr->disk_free < 1024U*1024U*1024U /* 1G */) { + color_on = ansi_highlight_red(); + color_off = ansi_normal(); + } else if (permille <= 250U && + hr->disk_free < 2U*1024U*1024U*1024U /* 2G */) { + color_on = ansi_highlight_yellow(); + color_off = ansi_normal(); + } else + color_on = color_off = ""; + + printf(" Disk Free: %s%s (= %u.%01u%%)%s\n", + color_on, + format_bytes(buf, sizeof(buf), hr->disk_free), + permille / 10, permille % 10, + color_off); + } else + printf(" Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free)); } if (hr->disk_floor != UINT64_MAX) { @@ -436,10 +472,13 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { STRV_FOREACH(i, hr->pkcs11_token_uri) printf(i == hr->pkcs11_token_uri ? - " Sec. Token: %s\n" : + "PKCS11 Token: %s\n" : " %s\n", *i); } + if (hr->n_fido2_hmac_credential > 0) + printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential); + k = strv_length(hr->hashed_password); if (k == 0) printf(" Passwords: %snone%s\n", diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 080c2a140..16edaa45f 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -52,6 +52,7 @@ UserRecord* user_record_new(void) { .nodev = true, .nosuid = true, .luks_discard = -1, + .luks_offline_discard = -1, .luks_volume_key_size = UINT64_MAX, .luks_pbkdf_time_cost_usec = UINT64_MAX, .luks_pbkdf_memory_cost = UINT64_MAX, @@ -80,6 +81,7 @@ UserRecord* user_record_new(void) { .password_change_inactive_usec = UINT64_MAX, .password_change_now = -1, .pkcs11_protected_authentication_path_permitted = -1, + .fido2_user_presence_permitted = -1, }; return h; @@ -94,6 +96,22 @@ static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) { erase_and_free(k->hashed_password); } +static void fido2_hmac_credential_done(Fido2HmacCredential *c) { + if (!c) + return; + + free(c->id); +} + +static void fido2_hmac_salt_done(Fido2HmacSalt *s) { + if (!s) + return; + + fido2_hmac_credential_done(&s->credential); + erase_and_free(s->salt); + erase_and_free(s->hashed_password); +} + static UserRecord* user_record_free(UserRecord *h) { if (!h) return NULL; @@ -119,7 +137,7 @@ static UserRecord* user_record_free(UserRecord *h) { strv_free_erase(h->hashed_password); strv_free_erase(h->ssh_authorized_keys); strv_free_erase(h->password); - strv_free_erase(h->pkcs11_pin); + strv_free_erase(h->token_pin); free(h->cifs_service); free(h->cifs_user_name); @@ -146,6 +164,11 @@ static UserRecord* user_record_free(UserRecord *h) { pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i); free(h->pkcs11_encrypted_key); + for (size_t i = 0; i < h->n_fido2_hmac_credential; i++) + fido2_hmac_credential_done(h->fido2_hmac_credential + i); + for (size_t i = 0; i < h->n_fido2_hmac_salt; i++) + fido2_hmac_salt_done(h->fido2_hmac_salt + i); + json_variant_unref(h->json); return mfree(h); @@ -619,8 +642,10 @@ static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchF static const JsonDispatch secret_dispatch_table[] = { { "password", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, password), 0 }, - { "pkcs11Pin", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, pkcs11_pin), 0 }, + { "tokenPin", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, token_pin), 0 }, + { "pkcs11Pin", /* legacy alias */ _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, token_pin), 0 }, { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 }, + { "fido2UserPresencePermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted), 0 }, {}, }; @@ -705,7 +730,7 @@ static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, Json int r; if (json_variant_is_null(variant)) { - k->data = mfree(k->data); + k->data = erase_and_free(k->data); k->size = 0; return 0; } @@ -765,13 +790,141 @@ static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispa return 0; } +static int dispatch_fido2_hmac_credential(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + Fido2HmacCredential *k = userdata; + size_t l; + void *b; + int r; + + if (json_variant_is_null(variant)) { + k->id = mfree(k->id); + k->size = 0; + return 0; + } + + if (!json_variant_is_string(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); + + r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l); + if (r < 0) + return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m"); + + free_and_replace(k->id, b); + k->size = l; + + return 0; +} + +static int dispatch_fido2_hmac_credential_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + UserRecord *h = userdata; + JsonVariant *e; + int r; + + if (!json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name)); + + JSON_VARIANT_ARRAY_FOREACH(e, variant) { + Fido2HmacCredential *array; + size_t l; + void *b; + + if (!json_variant_is_string(e)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string."); + + array = reallocarray(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1, sizeof(Fido2HmacCredential)); + if (!array) + return log_oom(); + + r = unbase64mem(json_variant_string(e), (size_t) -1, &b, &l); + if (r < 0) + return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m"); + + h->fido2_hmac_credential = array; + + h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) { + .id = b, + .size = l, + }; + } + + return 0; +} + +static int dispatch_fido2_hmac_salt_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + Fido2HmacSalt *k = userdata; + size_t l; + void *b; + int r; + + if (json_variant_is_null(variant)) { + k->salt = erase_and_free(k->salt); + k->salt_size = 0; + return 0; + } + + if (!json_variant_is_string(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); + + r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l); + if (r < 0) + return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m"); + + erase_and_free(k->salt); + k->salt = b; + k->salt_size = l; + + return 0; +} + +static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + UserRecord *h = userdata; + JsonVariant *e; + int r; + + if (!json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name)); + + JSON_VARIANT_ARRAY_FOREACH(e, variant) { + Fido2HmacSalt *array, *k; + + static const JsonDispatch fido2_hmac_salt_dispatch_table[] = { + { "credential", JSON_VARIANT_STRING, dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential), JSON_MANDATORY }, + { "salt", JSON_VARIANT_STRING, dispatch_fido2_hmac_salt_value, 0, JSON_MANDATORY }, + { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY }, + {}, + }; + + if (!json_variant_is_object(e)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object."); + + array = reallocarray(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1, sizeof(Fido2HmacSalt)); + if (!array) + return log_oom(); + + h->fido2_hmac_salt = array; + k = h->fido2_hmac_salt + h->n_fido2_hmac_salt; + *k = (Fido2HmacSalt) {}; + + r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k); + if (r < 0) { + fido2_hmac_salt_done(k); + return r; + } + + h->n_fido2_hmac_salt++; + } + + return 0; +} + static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { static const JsonDispatch privileged_dispatch_table[] = { - { "passwordHint", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, password_hint), 0 }, - { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, hashed_password), JSON_SAFE }, - { "sshAuthorizedKeys", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, ssh_authorized_keys), 0 }, - { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY, dispatch_pkcs11_key, 0, 0 }, + { "passwordHint", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, password_hint), 0 }, + { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, hashed_password), JSON_SAFE }, + { "sshAuthorizedKeys", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, ssh_authorized_keys), 0 }, + { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY, dispatch_pkcs11_key, 0, 0 }, + { "fido2HmacSalt", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_salt, 0, 0 }, {}, }; @@ -905,65 +1058,67 @@ int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) { static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { static const JsonDispatch per_machine_dispatch_table[] = { - { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, - { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, - { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE }, - { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 }, - { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, - { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, - { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 }, - { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE }, - { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE }, - { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 }, - { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 }, - { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 }, - { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 }, - { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 }, - { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 }, - { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 }, - { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, - { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 }, - { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, - { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, - { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, - { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, - { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 }, - { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 }, - { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 }, - { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 }, - { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 }, - { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE }, - { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE }, - { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE }, - { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 }, - { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 }, - { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 }, - { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX}, - { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE }, - { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 }, - { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 }, - { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 }, - { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0, }, - { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE }, - { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE }, - { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 }, - { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE }, - { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE }, - { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, - { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, - { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, - { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 }, - { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 }, - { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 }, - { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 }, - { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 }, - { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 }, - { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 }, - { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 }, - { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 }, - { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 }, - { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 }, - { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 }, + { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, + { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, + { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE }, + { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 }, + { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, + { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, + { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 }, + { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE }, + { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE }, + { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 }, + { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 }, + { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 }, + { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 }, + { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 }, + { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 }, + { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 }, + { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, + { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 }, + { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, + { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, + { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, + { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, + { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 }, + { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 }, + { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 }, + { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 }, + { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 }, + { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE }, + { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE }, + { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE }, + { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 }, + { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 }, + { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 }, + { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX}, + { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE }, + { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 }, + { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 }, + { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 }, + { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0, }, + { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0, }, + { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE }, + { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE }, + { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 }, + { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE }, + { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE }, + { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, + { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, + { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, + { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 }, + { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 }, + { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 }, + { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 }, + { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 }, + { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 }, + { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 }, + { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 }, + { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 }, + { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 }, + { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 }, + { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 }, + { "fido2HmacCredential", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 }, {}, }; @@ -1057,7 +1212,34 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF return json_dispatch(m, status_dispatch_table, NULL, flags, userdata); } +int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) { + const char *suffix; + char *z; + + assert(storage >= 0); + assert(user_name_and_realm); + assert(ret); + + if (storage == USER_LUKS) + suffix = ".home"; + else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) + suffix = ".homedir"; + else { + *ret = NULL; + return 0; + } + + z = strjoin("/home/", user_name_and_realm, suffix); + if (!z) + return -ENOMEM; + + *ret = z; + return 1; +} + static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) { + int r; + assert(h); if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR)) @@ -1071,7 +1253,7 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) { return json_log_oom(h->json, json_flags); } - /* Let's add in the following automatisms only for regular users, they dont make sense for any others */ + /* Let's add in the following automatisms only for regular users, they don't make sense for any others */ if (user_record_disposition(h) != USER_REGULAR) return 0; @@ -1082,22 +1264,9 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) { } if (!h->image_path && !h->image_path_auto) { - const char *suffix; - UserStorage storage; - - storage = user_record_storage(h); - if (storage == USER_LUKS) - suffix = ".home"; - else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) - suffix = ".homedir"; - else - suffix = NULL; - - if (suffix) { - h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), suffix); - if (!h->image_path_auto) - return json_log_oom(h->json, json_flags); - } + r = user_record_build_image_path(user_record_storage(h), user_record_user_name_and_realm(h), &h->image_path_auto); + if (r < 0) + return json_log(h->json, json_flags, r, "Failed to determine default image path: %m"); } return 0; @@ -1231,83 +1400,85 @@ int user_group_record_mangle( int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) { static const JsonDispatch user_dispatch_table[] = { - { "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), JSON_RELAX}, - { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 }, - { "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 }, - { "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE }, - { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE }, - { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 }, - { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(UserRecord, disposition), 0 }, - { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 }, - { "lastPasswordChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 }, - { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, - { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, - { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 }, - { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE }, - { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE }, - { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 }, - { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 }, - { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 }, - { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 }, - { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 }, - { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 }, - { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 }, - { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, - { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 }, - { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, - { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, - { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, - { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, - { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 }, - { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 }, - { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 }, - { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 }, - { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 }, - { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE }, - { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE }, - { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE }, - { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 }, - { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 }, - { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 }, - { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 }, - { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX}, - { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE }, - { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 }, - { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 }, - { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 }, - { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 }, - { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE }, - { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE }, - { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 }, - { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE }, - { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE }, - { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, - { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, - { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, - { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE }, - { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 }, - { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 }, - { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 }, - { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 }, - { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 }, - { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 }, - { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 }, - { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 }, - { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 }, - { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 }, - { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 }, - { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 }, + { "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), JSON_RELAX}, + { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 }, + { "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 }, + { "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE }, + { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE }, + { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 }, + { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(UserRecord, disposition), 0 }, + { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 }, + { "lastPasswordChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 }, + { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 }, + { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 }, + { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 }, + { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE }, + { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE }, + { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 }, + { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 }, + { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 }, + { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 }, + { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 }, + { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 }, + { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 }, + { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 }, + { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 }, + { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 }, + { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 }, + { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 }, + { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 }, + { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 }, + { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 }, + { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 }, + { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 }, + { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 }, + { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE }, + { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE }, + { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE }, + { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 }, + { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 }, + { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 }, + { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 }, + { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX}, + { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE }, + { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 }, + { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 }, + { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 }, + { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 }, + { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 }, + { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE }, + { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE }, + { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 }, + { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE }, + { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE }, + { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, + { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, + { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, + { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE }, + { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 }, + { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 }, + { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 }, + { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 }, + { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 }, + { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 }, + { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 }, + { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 }, + { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 }, + { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 }, + { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 }, + { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 }, + { "fido2HmacCredential", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 }, - { "secret", JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 }, - { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 }, + { "secret", JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 }, + { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 }, /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */ - { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 }, - { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 }, - { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 }, + { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 }, + { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 }, + { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 }, /* Ignore 'signature', we check it with explicit accessors instead */ - { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 }, + { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 }, {}, }; @@ -1500,6 +1671,27 @@ bool user_record_luks_discard(UserRecord *h) { return path_startswith(ip, "/dev/"); } +bool user_record_luks_offline_discard(UserRecord *h) { + const char *ip; + + assert(h); + + if (h->luks_offline_discard >= 0) + return h->luks_offline_discard; + + /* Discard while we are logged out should generally be a good idea, except when operating directly on + * physical media, where we should just bind it to the online discard mode. */ + + ip = user_record_image_path(h); + if (!ip) + return false; + + if (path_startswith(ip, "/dev/")) + return user_record_luks_discard(h); + + return true; +} + const char *user_record_luks_cipher(UserRecord *h) { assert(h); @@ -1646,6 +1838,9 @@ bool user_record_can_authenticate(UserRecord *h) { if (h->n_pkcs11_encrypted_key > 0) return true; + if (h->n_fido2_hmac_salt > 0) + return true; + return !strv_isempty(h->hashed_password); } @@ -1802,7 +1997,7 @@ int user_record_test_password_change_required(UserRecord *h) { 0: No password change required, but permitted */ - /* If a pasword change request has been set explicitly, it overrides everything */ + /* If a password change request has been set explicitly, it overrides everything */ if (h->password_change_now > 0) return -EKEYREVOKED; diff --git a/src/shared/user-record.h b/src/shared/user-record.h index 5bac30476..1bfd095d2 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -189,6 +189,23 @@ typedef struct Pkcs11EncryptedKey { char *hashed_password; } Pkcs11EncryptedKey; +typedef struct Fido2HmacCredential { + void *id; + size_t size; +} Fido2HmacCredential; + +typedef struct Fido2HmacSalt { + /* The FIDO2 Cridential ID to use */ + Fido2HmacCredential credential; + + /* The FIDO2 salt value */ + void *salt; + size_t salt_size; + + /* What to test the hashed salt value against, usually UNIX password hash here. */ + char *hashed_password; +} Fido2HmacSalt; + typedef struct UserRecord { /* The following three fields are not part of the JSON record */ unsigned n_ref; @@ -239,7 +256,7 @@ typedef struct UserRecord { char **hashed_password; char **ssh_authorized_keys; char **password; - char **pkcs11_pin; + char **token_pin; char *cifs_domain; char *cifs_user_name; @@ -261,6 +278,7 @@ typedef struct UserRecord { sd_id128_t file_system_uuid; int luks_discard; + int luks_offline_discard; char *luks_cipher; char *luks_cipher_mode; uint64_t luks_volume_key_size; @@ -308,6 +326,12 @@ typedef struct UserRecord { size_t n_pkcs11_encrypted_key; int pkcs11_protected_authentication_path_permitted; + Fido2HmacCredential *fido2_hmac_credential; + size_t n_fido2_hmac_credential; + Fido2HmacSalt *fido2_hmac_salt; + size_t n_fido2_hmac_salt; + int fido2_user_presence_permitted; + JsonVariant *json; } UserRecord; @@ -332,6 +356,7 @@ const char *user_record_cifs_user_name(UserRecord *h); const char *user_record_shell(UserRecord *h); const char *user_record_real_name(UserRecord *h); bool user_record_luks_discard(UserRecord *h); +bool user_record_luks_offline_discard(UserRecord *h); const char *user_record_luks_cipher(UserRecord *h); const char *user_record_luks_cipher_mode(UserRecord *h); uint64_t user_record_luks_volume_key_size(UserRecord *h); @@ -347,6 +372,8 @@ usec_t user_record_ratelimit_interval_usec(UserRecord *h); uint64_t user_record_ratelimit_burst(UserRecord *h); bool user_record_can_authenticate(UserRecord *h); +int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret); + bool user_record_equal(UserRecord *a, UserRecord *b); bool user_record_compatible(UserRecord *a, UserRecord *b); int user_record_compare_last_change(UserRecord *a, UserRecord *b); diff --git a/src/shared/userdb.c b/src/shared/userdb.c index 0769a792c..94120862d 100644 --- a/src/shared/userdb.c +++ b/src/shared/userdb.c @@ -3,6 +3,7 @@ #include #include "dirent-util.h" +#include "dlfcn-util.h" #include "errno-util.h" #include "fd-util.h" #include "group-record-nss.h" @@ -32,8 +33,8 @@ struct UserDBIterator { bool nss_iterating:1; bool synthesize_root:1; bool synthesize_nobody:1; + bool nss_systemd_blocked:1; int error; - int nss_lock; unsigned n_found; sd_event *event; UserRecord *found_user; /* when .what == LOOKUP_USER */ @@ -85,7 +86,9 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) { } sd_event_unref(iterator->event); - safe_close(iterator->nss_lock); + + if (iterator->nss_systemd_blocked) + assert_se(userdb_block_nss_systemd(false) >= 0); return mfree(iterator); } @@ -102,12 +105,27 @@ static UserDBIterator* userdb_iterator_new(LookupWhat what) { *i = (UserDBIterator) { .what = what, - .nss_lock = -1, }; return i; } +static int userdb_iterator_block_nss_systemd(UserDBIterator *iterator) { + int r; + + assert(iterator); + + if (iterator->nss_systemd_blocked) + return 0; + + r = userdb_block_nss_systemd(true); + if (r < 0) + return r; + + iterator->nss_systemd_blocked = true; + return 1; +} + struct user_group_data { JsonVariant *record; bool incomplete; @@ -138,6 +156,8 @@ static int userdb_on_query_reply( r = -ESRCH; else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable")) r = -EHOSTDOWN; + else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported")) + r = -EOPNOTSUPP; else if (streq(error_id, VARLINK_ERROR_TIMEOUT)) r = -ETIMEDOUT; else @@ -359,15 +379,9 @@ static int userdb_connect( if (r < 0) return log_debug_errno(r, "Failed to invoke varlink method: %m"); - r = set_ensure_allocated(&iterator->links, &link_hash_ops); - if (r < 0) - return log_debug_errno(r, "Failed to allocate set: %m"); - - r = set_put(iterator->links, vl); + r = set_ensure_consume(&iterator->links, &link_hash_ops, TAKE_PTR(vl)); if (r < 0) return log_debug_errno(r, "Failed to add varlink connection to set: %m"); - - TAKE_PTR(vl); return r; } @@ -606,15 +620,13 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) { return r; } - if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) { - /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we - * already took the lock from our thread, which is totally OK.) */ - r = userdb_nss_compat_disable(); - if (r >= 0 || r == -EBUSY) { - iterator->nss_lock = r; + if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) { + /* Make sure the NSS lookup doesn't recurse back to us. */ + r = userdb_iterator_block_nss_systemd(iterator); + if (r >= 0) { /* Client-side NSS fallback */ - r = nss_user_record_by_name(name, ret); + r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret); if (r >= 0) return r; } @@ -655,13 +667,11 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) { return r; } - if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) { - r = userdb_nss_compat_disable(); - if (r >= 0 || r == -EBUSY) { - iterator->nss_lock = r; - + if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) { + r = userdb_iterator_block_nss_systemd(iterator); + if (r >= 0) { /* Client-side NSS fallback */ - r = nss_user_record_by_uid(uid, ret); + r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret); if (r >= 0) return r; } @@ -693,9 +703,9 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) { r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags); if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) { - iterator->nss_lock = userdb_nss_compat_disable(); - if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY) - return iterator->nss_lock; + r = userdb_iterator_block_nss_systemd(iterator); + if (r < 0) + return r; setpwent(); iterator->nss_iterating = true; @@ -815,11 +825,9 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) { } if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) { - r = userdb_nss_compat_disable(); - if (r >= 0 || r == -EBUSY) { - iterator->nss_lock = r; - - r = nss_group_record_by_name(name, ret); + r = userdb_iterator_block_nss_systemd(iterator); + if (r >= 0) { + r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret); if (r >= 0) return r; } @@ -861,11 +869,9 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) { } if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) { - r = userdb_nss_compat_disable(); - if (r >= 0 || r == -EBUSY) { - iterator->nss_lock = r; - - r = nss_group_record_by_gid(gid, ret); + r = userdb_iterator_block_nss_systemd(iterator); + if (r >= 0) { + r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret); if (r >= 0) return r; } @@ -897,9 +903,9 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) { r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags); if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) { - iterator->nss_lock = userdb_nss_compat_disable(); - if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY) - return iterator->nss_lock; + r = userdb_iterator_block_nss_systemd(iterator); + if (r < 0) + return r; setgrent(); iterator->nss_iterating = true; @@ -998,9 +1004,9 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS)) goto finish; - iterator->nss_lock = userdb_nss_compat_disable(); - if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY) - return iterator->nss_lock; + r = userdb_iterator_block_nss_systemd(iterator); + if (r < 0) + return r; iterator->filter_user_name = strdup(name); if (!iterator->filter_user_name) @@ -1041,12 +1047,12 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator ** if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS)) goto finish; - iterator->nss_lock = userdb_nss_compat_disable(); - if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY) - return iterator->nss_lock; + r = userdb_iterator_block_nss_systemd(iterator); + if (r < 0) + return r; /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */ - (void) nss_group_record_by_name(name, &gr); + (void) nss_group_record_by_name(name, false, &gr); if (gr) { iterator->members_of_group = strv_copy(gr->members); if (!iterator->members_of_group) @@ -1082,9 +1088,9 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) { if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS)) goto finish; - iterator->nss_lock = userdb_nss_compat_disable(); - if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY) - return iterator->nss_lock; + r = userdb_iterator_block_nss_systemd(iterator); + if (r < 0) + return r; setgrent(); iterator->nss_iterating = true; @@ -1221,115 +1227,24 @@ int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret) return 0; } -static int userdb_thread_sockaddr(struct sockaddr_un *ret_sa, socklen_t *ret_salen) { - static const uint8_t - k1[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 }, - k2[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b }; +int userdb_block_nss_systemd(int b) { + _cleanup_(dlclosep) void *dl = NULL; + int (*call)(bool b); - struct siphash sh; - uint64_t x, y; - pid_t tid; - void *p; + /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */ - assert(ret_sa); - assert(ret_salen); - - /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an - * indicator whether to emulate NSS records for complex user records that are also available via the - * varlink protocol. The name of the socket is picked in a way so that: - * - * → it is per-thread (by hashing from the TID) - * - * → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM - * value every process gets passed from the kernel - * - * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only, - * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract - * namespace the lock is automatically cleaned up when the process dies abnormally. - * - */ - - p = ULONG_TO_PTR(getauxval(AT_RANDOM)); - if (!p) - return -EIO; - - tid = gettid(); - - siphash24_init(&sh, k1); - siphash24_compress(p, 16, &sh); - siphash24_compress(&tid, sizeof(tid), &sh); - x = siphash24_finalize(&sh); - - siphash24_init(&sh, k2); - siphash24_compress(p, 16, &sh); - siphash24_compress(&tid, sizeof(tid), &sh); - y = siphash24_finalize(&sh); - - *ret_sa = (struct sockaddr_un) { - .sun_family = AF_UNIX, - }; - - sprintf(ret_sa->sun_path + 1, "userdb-%016" PRIx64 "%016" PRIx64, x, y); - *ret_salen = offsetof(struct sockaddr_un, sun_path) + 1 + 7 + 32; - - return 0; -} - -int userdb_nss_compat_is_enabled(void) { - _cleanup_close_ int fd = -1; - union sockaddr_union sa; - socklen_t salen; - int r; - - /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns - * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex - * user records. */ - - r = userdb_thread_sockaddr(&sa.un, &salen); - if (r < 0) - return r; - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - /* Try to connect(). This doesn't do anything really, except that it checks whether the socket - * address is bound at all. */ - if (connect(fd, &sa.sa, salen) < 0) { - if (errno == ECONNREFUSED) /* the socket is not bound, hence NSS emulation shall be done */ - return true; - - return -errno; + dl = dlopen(ROOTLIBDIR "/libnss_systemd.so.2", RTLD_LAZY|RTLD_NODELETE); + if (!dl) { + /* If the file isn't installed, don't complain loudly */ + log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror()); + return 0; } - return false; -} - -int userdb_nss_compat_disable(void) { - _cleanup_close_ int fd = -1; - union sockaddr_union sa; - socklen_t salen; - int r; - - /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are - * synthesized for all complex user records looked up via NSS. If this call is invoked this is - * disabled for the invoking thread, but only for it. A caller that natively supports the varlink - * user record protocol may use that to turn off the compatibility for NSS lookups. */ - - r = userdb_thread_sockaddr(&sa.un, &salen); - if (r < 0) - return r; - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return -errno; - - if (bind(fd, &sa.sa, salen) < 0) { - if (errno == EADDRINUSE) /* lock already taken, convert this into a recognizable error */ - return -EBUSY; - - return -errno; - } - - return TAKE_FD(fd); + call = (int (*)(bool b)) dlsym(dl, "_nss_systemd_block"); + if (!call) + /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */ + return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD), + "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror()); + + return call(b); } diff --git a/src/shared/userdb.h b/src/shared/userdb.h index 4288b0ff9..2464f54c3 100644 --- a/src/shared/userdb.h +++ b/src/shared/userdb.h @@ -16,9 +16,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free); typedef enum UserDBFlags { USERDB_AVOID_NSS = 1 << 0, /* don't do client-side nor server-side NSS */ - USERDB_AVOID_DYNAMIC_USER = 1 << 1, /* exclude looking up in io.systemd.DynamicUser */ - USERDB_AVOID_MULTIPLEXER = 1 << 2, /* exclude looking up via io.systemd.Multiplexer */ - USERDB_DONT_SYNTHESIZE = 1 << 3, /* don't synthesize root/nobody */ + USERDB_AVOID_SHADOW = 1 << 1, /* don't do client-side shadow calls (server side might happen though) */ + USERDB_AVOID_DYNAMIC_USER = 1 << 2, /* exclude looking up in io.systemd.DynamicUser */ + USERDB_AVOID_MULTIPLEXER = 1 << 3, /* exclude looking up via io.systemd.Multiplexer */ + USERDB_DONT_SYNTHESIZE = 1 << 4, /* don't synthesize root/nobody */ } UserDBFlags; int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret); @@ -37,5 +38,4 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret); int membershipdb_iterator_get(UserDBIterator *iterator, char **user, char **group); int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret); -int userdb_nss_compat_is_enabled(void); -int userdb_nss_compat_disable(void); +int userdb_block_nss_systemd(int b); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index fa4f32f35..9edcd8302 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -14,6 +14,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "hostname-util.h" +#include "io-util.h" #include "macro.h" #include "memory-util.h" #include "path-util.h" @@ -24,8 +25,8 @@ #include "utmp-wtmp.h" int utmp_get_runlevel(int *runlevel, int *previous) { + _cleanup_(utxent_cleanup) bool utmpx = false; struct utmpx *found, lookup = { .ut_type = RUN_LVL }; - int r; const char *e; assert(runlevel); @@ -34,8 +35,7 @@ int utmp_get_runlevel(int *runlevel, int *previous) { * precedence. Presumably, sysvinit does this to work around a * race condition that would otherwise exist where we'd always * go to disk and hence might read runlevel data that might be - * very new and does not apply to the current script being - * executed. */ + * very new and not apply to the current script being executed. */ e = getenv("RUNLEVEL"); if (e && e[0] > 0) { @@ -57,27 +57,17 @@ int utmp_get_runlevel(int *runlevel, int *previous) { if (utmpxname(_PATH_UTMPX) < 0) return -errno; - setutxent(); + utmpx = utxent_start(); found = getutxid(&lookup); if (!found) - r = -errno; - else { - int a, b; + return -errno; - a = found->ut_pid & 0xFF; - b = (found->ut_pid >> 8) & 0xFF; + *runlevel = found->ut_pid & 0xFF; + if (previous) + *previous = (found->ut_pid >> 8) & 0xFF; - *runlevel = a; - if (previous) - *previous = b; - - r = 0; - } - - endutxent(); - - return r; + return 0; } static void init_timestamp(struct utmpx *store, usec_t t) { @@ -105,7 +95,7 @@ static void init_entry(struct utmpx *store, usec_t t) { } static int write_entry_utmp(const struct utmpx *store) { - int r; + _cleanup_(utxent_cleanup) bool utmpx = false; assert(store); @@ -116,26 +106,35 @@ static int write_entry_utmp(const struct utmpx *store) { if (utmpxname(_PATH_UTMPX) < 0) return -errno; - setutxent(); + utmpx = utxent_start(); - if (!pututxline(store)) - r = -errno; - else - r = 0; - - endutxent(); - - return r; + if (pututxline(store)) + return 0; + if (errno == ENOENT) { + /* If utmp/wtmp have been disabled, that's a good thing, hence ignore the error. */ + log_debug_errno(errno, "Not writing utmp: %m"); + return 0; + } + return -errno; } static int write_entry_wtmp(const struct utmpx *store) { assert(store); /* wtmp is a simple append-only file where each entry is - simply appended to the end; i.e. basically a log. */ + * simply appended to the end; i.e. basically a log. */ errno = 0; updwtmpx(_PATH_WTMPX, store); + if (errno == ENOENT) { + /* If utmp/wtmp have been disabled, that's a good thing, hence ignore the error. */ + log_debug_errno(errno, "Not writing wtmp: %m"); + return 0; + } + if (errno == EROFS) { + log_warning_errno(errno, "Failed to write wtmp record, ignoring: %m"); + return 0; + } return -errno; } @@ -144,16 +143,7 @@ static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *s r = write_entry_utmp(store_utmp); s = write_entry_wtmp(store_wtmp); - - if (r >= 0) - r = s; - - /* If utmp/wtmp have been disabled, that's a good thing, hence - * ignore the errors */ - if (r == -ENOENT) - r = 0; - - return r; + return r < 0 ? r : s; } static int write_entry_both(const struct utmpx *store) { @@ -297,7 +287,7 @@ int utmp_put_runlevel(int runlevel, int previous) { return write_entry_both(&store); } -#define TIMEOUT_MSEC 50 +#define TIMEOUT_USEC (50 * USEC_PER_MSEC) static int write_to_terminal(const char *tty, const char *message) { _cleanup_close_ int fd = -1; @@ -315,14 +305,10 @@ static int write_to_terminal(const char *tty, const char *message) { p = message; left = strlen(message); - end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; + end = now(CLOCK_MONOTONIC) + TIMEOUT_USEC; while (left > 0) { ssize_t n; - struct pollfd pollfd = { - .fd = fd, - .events = POLLOUT, - }; usec_t t; int k; @@ -331,10 +317,9 @@ static int write_to_terminal(const char *tty, const char *message) { if (t >= end) return -ETIME; - k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC); + k = fd_wait_for_event(fd, POLLOUT, end - t); if (k < 0) - return -errno; - + return k; if (k == 0) return -ETIME; diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 9e433cf73..fe55bad12 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -8,6 +8,8 @@ #include "util.h" #if ENABLE_UTMP +#include + int utmp_get_runlevel(int *runlevel, int *previous); int utmp_put_shutdown(void); @@ -24,6 +26,15 @@ int utmp_wall( bool (*match_tty)(const char *tty, void *userdata), void *userdata); +static inline bool utxent_start(void) { + setutxent(); + return true; +} +static inline void utxent_cleanup(bool *initialized) { + if (initialized) + endutxent(); +} + #else /* ENABLE_UTMP */ static inline int utmp_get_runlevel(int *runlevel, int *previous) { diff --git a/src/shared/varlink.c b/src/shared/varlink.c index dff7d3253..be3559dc1 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -6,6 +6,7 @@ #include "errno-util.h" #include "fd-util.h" #include "hashmap.h" +#include "io-util.h" #include "list.h" #include "process-util.h" #include "set.h" @@ -386,7 +387,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Varlink, varlink, varlink_destroy); static int varlink_test_disconnect(Varlink *v) { assert(v); - /* Tests whether we the the connection has been terminated. We are careful to not stop processing it + /* Tests whether we the connection has been terminated. We are careful to not stop processing it * prematurely, since we want to handle half-open connections as well as possible and want to flush * out and read data before we close down if we can. */ @@ -1003,8 +1004,6 @@ static void handle_revents(Varlink *v, int revents) { } int varlink_wait(Varlink *v, usec_t timeout) { - struct timespec ts; - struct pollfd pfd; int r, fd, events; usec_t t; @@ -1038,20 +1037,14 @@ int varlink_wait(Varlink *v, usec_t timeout) { if (events < 0) return events; - pfd = (struct pollfd) { - .fd = fd, - .events = events, - }; - - r = ppoll(&pfd, 1, - t == USEC_INFINITY ? NULL : timespec_store(&ts, t), - NULL); + r = fd_wait_for_event(fd, events, t); if (r < 0) - return -errno; + return r; + if (r == 0) + return 0; - handle_revents(v, pfd.revents); - - return r > 0 ? 1 : 0; + handle_revents(v, r); + return 1; } int varlink_get_fd(Varlink *v) { @@ -1119,8 +1112,6 @@ int varlink_flush(Varlink *v) { return -ENOTCONN; for (;;) { - struct pollfd pfd; - if (v->output_buffer_size == 0) break; if (v->write_disconnected) @@ -1134,15 +1125,13 @@ int varlink_flush(Varlink *v) { continue; } - pfd = (struct pollfd) { - .fd = v->fd, - .events = POLLOUT, - }; + r = fd_wait_for_event(v->fd, POLLOUT, USEC_INFINITY); + if (r < 0) + return r; - if (poll(&pfd, 1, -1) < 0) - return -errno; + assert(r != 0); - handle_revents(v, pfd.revents); + handle_revents(v, r); } return ret; @@ -1334,7 +1323,7 @@ int varlink_invoke(Varlink *v, const char *method, JsonVariant *parameters) { if (v->state == VARLINK_DISCONNECTED) return -ENOTCONN; - /* We allow enqueing multiple method calls at once! */ + /* We allow enqueuing multiple method calls at once! */ if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY)) return -EBUSY; diff --git a/src/shared/verbs.c b/src/shared/verbs.c index 06d89b473..d2744b691 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -45,10 +45,19 @@ bool running_in_chroot_or_offline(void) { return r > 0; } +const Verb* verbs_find_verb(const char *name, const Verb verbs[]) { + for (size_t i = 0; verbs[i].dispatch; i++) + if (streq_ptr(name, verbs[i].verb) || + (!name && FLAGS_SET(verbs[i].flags, VERB_DEFAULT))) + return &verbs[i]; + + /* At the end of the list? */ + return NULL; +} + int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { const Verb *verb; const char *name; - unsigned i; int left; assert(verbs); @@ -62,31 +71,16 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { optind = 0; name = argv[0]; - for (i = 0;; i++) { - bool found; - - /* At the end of the list? */ - if (!verbs[i].dispatch) { - if (name) - log_error("Unknown command verb %s.", name); - else - log_error("Command verb required."); - return -EINVAL; - } - + verb = verbs_find_verb(name, verbs); + if (!verb) { if (name) - found = streq(name, verbs[i].verb); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown command verb %s.", name); else - found = verbs[i].flags & VERB_DEFAULT; - - if (found) { - verb = &verbs[i]; - break; - } + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Command verb required."); } - assert(verb); - if (!name) left = 1; @@ -101,10 +95,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { "Too many arguments."); if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) { - if (name) - log_info("Running in chroot, ignoring request: %s", name); - else - log_info("Running in chroot, ignoring request."); + log_info("Running in chroot, ignoring command '%s'", name ?: verb->verb); return 0; } diff --git a/src/shared/verbs.h b/src/shared/verbs.h index c5fe6cc7c..b6a1afcde 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -6,8 +6,8 @@ #define VERB_ANY ((unsigned) -1) typedef enum VerbFlags { - VERB_DEFAULT = 1 << 0, - VERB_ONLINE_ONLY = 1 << 1, + VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */ + VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */ } VerbFlags; typedef struct { @@ -19,4 +19,5 @@ typedef struct { bool running_in_chroot_or_offline(void); +const Verb* verbs_find_verb(const char *name, const Verb verbs[]); int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 98fefb395..4d3d8828f 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -16,6 +16,7 @@ static int watchdog_fd = -1; static char *watchdog_device = NULL; static usec_t watchdog_timeout = USEC_INFINITY; +static usec_t watchdog_last_ping = USEC_INFINITY; static int update_timeout(void) { int r; @@ -57,6 +58,8 @@ static int update_timeout(void) { r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); if (r < 0) return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); + + watchdog_last_ping = now(clock_boottime_or_monotonic()); } return 0; @@ -114,9 +117,38 @@ int watchdog_set_timeout(usec_t *usec) { return r; } +usec_t watchdog_runtime_wait(void) { + usec_t rtwait; + usec_t ntime; + + if (!timestamp_is_set(watchdog_timeout)) + return USEC_INFINITY; + + /* Sleep half the watchdog timeout since the last successful ping at most */ + if (timestamp_is_set(watchdog_last_ping)) { + ntime = now(clock_boottime_or_monotonic()); + assert(ntime >= watchdog_last_ping); + rtwait = usec_sub_unsigned(watchdog_last_ping + (watchdog_timeout / 2), ntime); + } else + rtwait = watchdog_timeout / 2; + + return rtwait; +} + int watchdog_ping(void) { + usec_t ntime; int r; + ntime = now(clock_boottime_or_monotonic()); + + /* Never ping earlier than watchdog_timeout/4 and try to ping + * by watchdog_timeout/2 plus scheduling latencies the latest */ + if (timestamp_is_set(watchdog_last_ping)) { + assert(ntime >= watchdog_last_ping); + if ((ntime - watchdog_last_ping) < (watchdog_timeout / 4)) + return 0; + } + if (watchdog_fd < 0) { r = open_watchdog(); if (r < 0) @@ -127,6 +159,8 @@ int watchdog_ping(void) { if (r < 0) return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); + watchdog_last_ping = ntime; + return 0; } diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h index a345e4ba7..ce739fd8a 100644 --- a/src/shared/watchdog.h +++ b/src/shared/watchdog.h @@ -10,6 +10,7 @@ int watchdog_set_device(char *path); int watchdog_set_timeout(usec_t *usec); int watchdog_ping(void); void watchdog_close(bool disarm); +usec_t watchdog_runtime_wait(void); static inline void watchdog_free_device(void) { (void) watchdog_set_device(NULL); diff --git a/src/shared/wifi-util.c b/src/shared/wifi-util.c index 2717db650..22bb3041f 100644 --- a/src/shared/wifi-util.c +++ b/src/shared/wifi-util.c @@ -14,6 +14,9 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp sd_genl_family family; int r; + assert(genl); + assert(ifindex > 0); + r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_INTERFACE, &m); if (r < 0) return log_debug_errno(r, "Failed to create generic netlink message: %m"); @@ -23,10 +26,17 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp return log_debug_errno(r, "Could not append NL80211_ATTR_IFINDEX attribute: %m"); r = sd_netlink_call(genl, m, 0, &reply); + if (r == -ENODEV) { + /* For obsolete WEXT driver. */ + log_debug_errno(r, "Failed to request information about wifi interface %d. " + "The device doesn't seem to have nl80211 interface. Ignoring.", + ifindex); + goto nodata; + } if (r < 0) return log_debug_errno(r, "Failed to request information about wifi interface %d: %m", ifindex); if (!reply) - return 0; + goto nodata; r = sd_netlink_message_get_errno(reply); if (r < 0) @@ -37,7 +47,7 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp return log_debug_errno(r, "Failed to determine genl family: %m"); if (family != SD_GENL_NL80211) { log_debug("Received message of unexpected genl family %u, ignoring.", family); - return 0; + goto nodata; } if (iftype) { @@ -51,11 +61,20 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp if (ssid) { r = sd_netlink_message_read_string_strdup(reply, NL80211_ATTR_SSID, ssid); - if (r < 0 && r != -ENODATA) + if (r == -ENODATA) + goto nodata; + if (r < 0) return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m"); } - return r == -ENODATA ? 0 : 1; + return 1; + +nodata: + if (iftype) + *iftype = 0; + if (ssid) + *ssid = NULL; + return 0; } int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { @@ -63,6 +82,10 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { sd_genl_family family; int r; + assert(genl); + assert(ifindex > 0); + assert(bssid); + r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_STATION, &m); if (r < 0) return log_debug_errno(r, "Failed to create generic netlink message: %m"); @@ -79,7 +102,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { if (r < 0) return log_debug_errno(r, "Failed to request information about wifi station: %m"); if (!reply) - return 0; + goto nodata; r = sd_netlink_message_get_errno(reply); if (r < 0) @@ -90,12 +113,18 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { return log_debug_errno(r, "Failed to determine genl family: %m"); if (family != SD_GENL_NL80211) { log_debug("Received message of unexpected genl family %u, ignoring.", family); - return 0; + goto nodata; } r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, bssid); - if (r < 0 && r != -ENODATA) + if (r == -ENODATA) + goto nodata; + if (r < 0) return log_debug_errno(r, "Failed to get NL80211_ATTR_MAC attribute: %m"); - return r == -ENODATA ? 0 : 1; + return 1; + +nodata: + *bssid = (struct ether_addr) {}; + return 0; } diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 523040b57..06c9710c6 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -52,6 +52,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LOG_TARGET, ARG_LOG_COLOR, ARG_LOG_LOCATION, + ARG_LOG_TIME, ARG_EXIT_CODE, ARG_TIMEOUT, }; @@ -61,6 +62,7 @@ static int parse_argv(int argc, char *argv[]) { { "log-target", required_argument, NULL, ARG_LOG_TARGET }, { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "log-time", optional_argument, NULL, ARG_LOG_TIME }, { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, { "timeout", required_argument, NULL, ARG_TIMEOUT }, {} @@ -111,6 +113,17 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_LOG_TIME: + + if (optarg) { + r = log_show_time_from_string(optarg); + if (r < 0) + log_error_errno(r, "Failed to parse log time setting %s, ignoring: %m", optarg); + } else + log_show_time(true); + + break; + case ARG_EXIT_CODE: r = safe_atou8(optarg, &arg_exit_code); if (r < 0) @@ -549,6 +562,9 @@ int main(int argc, char *argv[]) { /* Child */ execv(args[0], (char * const *) args); + + /* execv failed (kexec binary missing?), so try simply reboot(RB_KEXEC) */ + (void) reboot(cmd); _exit(EXIT_FAILURE); } diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index cf716b4e4..7029352ca 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -23,6 +23,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "io-util.h" #include "log.h" #include "main-func.h" #include "parse-util.h" @@ -70,7 +71,7 @@ static int write_hibernate_location_info(const HibernateLocation *hibernate_loca return 0; } - return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m"); + return log_debug_errno(errno, "/sys/power/resume_offset not writable: %m"); } xsprintf(offset_str, "%" PRIu64, hibernate_location->offset); @@ -244,7 +245,6 @@ static int execute_s2h(const SleepConfig *sleep_config) { _cleanup_close_ int tfd = -1; char buf[FORMAT_TIMESPAN_MAX]; struct itimerspec ts = {}; - struct pollfd fds; int r; assert(sleep_config); @@ -266,19 +266,14 @@ static int execute_s2h(const SleepConfig *sleep_config) { if (r < 0) return r; - fds = (struct pollfd) { - .fd = tfd, - .events = POLLIN, - }; - r = poll(&fds, 1, 0); + r = fd_wait_for_event(tfd, POLLIN, 0); if (r < 0) - return log_error_errno(errno, "Error polling timerfd: %m"); + return log_error_errno(r, "Error polling timerfd: %m"); + if (!FLAGS_SET(r, POLLIN)) /* We woke up before the alarm time, we are done. */ + return 0; tfd = safe_close(tfd); - if (!FLAGS_SET(fds.revents, POLLIN)) /* We woke up before the alarm time, we are done. */ - return 0; - /* If woken up after alarm time, hibernate */ log_debug("Attempting to hibernate after waking from %s timer", format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC)); diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 2ee6fc2f0..b461aead6 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -31,10 +31,12 @@ static unsigned arg_connections_max = 256; static const char *arg_remote_host = NULL; +static usec_t arg_exit_idle_time = USEC_INFINITY; typedef struct Context { sd_event *event; sd_resolve *resolve; + sd_event_source *idle_time; Set *listen; Set *connections; @@ -75,6 +77,51 @@ static void connection_free(Connection *c) { free(c); } +static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) { + Context *c = userdata; + int r; + + if (!set_isempty(c->connections)) { + log_warning("Idle timer fired even though there are connections, ignoring"); + return 0; + } + + r = sd_event_exit(c->event, 0); + if (r < 0) { + log_warning_errno(r, "Error while stopping event loop, ignoring: %m"); + return 0; + } + return 0; +} + +static int connection_release(Connection *c) { + int r; + Context *context = c->context; + usec_t idle_instant; + + connection_free(c); + + if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) { + idle_instant = usec_add(now(CLOCK_MONOTONIC), arg_exit_idle_time); + if (context->idle_time) { + r = sd_event_source_set_time(context->idle_time, idle_instant); + if (r < 0) + return log_error_errno(r, "Error while setting idle time: %m"); + + r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "Error while enabling idle time: %m"); + } else { + r = sd_event_add_time(context->event, &context->idle_time, CLOCK_MONOTONIC, + idle_instant, 0, idle_time_cb, context); + if (r < 0) + return log_error_errno(r, "Failed to create idle timer: %m"); + } + } + + return 0; +} + static void context_clear(Context *context) { assert(context); @@ -83,6 +130,7 @@ static void context_clear(Context *context) { sd_event_unref(context->event); sd_resolve_unref(context->resolve); + sd_event_source_unref(context->idle_time); } static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) { @@ -206,7 +254,7 @@ static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userda return 1; quit: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -269,7 +317,7 @@ static int connection_complete(Connection *c) { return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -299,7 +347,7 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda return connection_complete(c); fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -343,7 +391,7 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -361,7 +409,7 @@ static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo * return connection_start(c, ai->ai_addr, ai->ai_addrlen); fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -409,7 +457,7 @@ static int resolve_remote(Connection *c) { return 0; fail: - connection_free(c); + connection_release(c); return 0; /* ignore errors, continue serving */ } @@ -426,25 +474,27 @@ static int add_connection_socket(Context *context, int fd) { return 0; } - r = set_ensure_allocated(&context->connections, NULL); - if (r < 0) { - log_oom(); - return 0; + if (context->idle_time) { + r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_OFF); + if (r < 0) + log_warning_errno(r, "Unable to disable idle timer, continuing: %m"); } - c = new0(Connection, 1); + c = new(Connection, 1); if (!c) { log_oom(); return 0; } - c->context = context; - c->server_fd = fd; - c->client_fd = -1; - c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1; - c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1; + *c = (Connection) { + .context = context, + .server_fd = fd, + .client_fd = -1, + .server_to_client_buffer = {-1, -1}, + .client_to_server_buffer = {-1, -1}, + }; - r = set_put(context->connections, c); + r = set_ensure_put(&context->connections, NULL, c); if (r < 0) { free(c); log_oom(); @@ -496,12 +546,6 @@ static int add_listen_socket(Context *context, int fd) { assert(context); assert(fd >= 0); - r = set_ensure_allocated(&context->listen, NULL); - if (r < 0) { - log_oom(); - return r; - } - r = sd_is_socket(fd, 0, SOCK_STREAM, 1); if (r < 0) return log_error_errno(r, "Failed to determine socket type: %m"); @@ -517,7 +561,7 @@ static int add_listen_socket(Context *context, int fd) { if (r < 0) return log_error_errno(r, "Failed to add event source: %m"); - r = set_put(context->listen, source); + r = set_ensure_put(&context->listen, NULL, source); if (r < 0) { log_error_errno(r, "Failed to add source to set: %m"); sd_event_source_unref(source); @@ -535,9 +579,13 @@ static int add_listen_socket(Context *context, int fd) { static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_free_ char *time_link = NULL; int r; r = terminal_urlify_man("systemd-socket-proxyd", "8", &link); + if (r < 0) + return log_oom(); + r = terminal_urlify_man("systemd.time", "7", &time_link); if (r < 0) return log_oom(); @@ -545,11 +593,14 @@ static int help(void) { "%1$s [SOCKET]\n\n" "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n" " -c --connections-max= Set the maximum number of connections to be accepted\n" + " --exit-idle-time= Exit when without a connection for this duration. See\n" + " the %3$s for time span format\n" " -h --help Show this help\n" " --version Show package version\n" "\nSee the %2$s for details.\n" , program_invocation_short_name , link + , time_link ); return 0; @@ -559,11 +610,13 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_EXIT_IDLE, ARG_IGNORE_ENV }; static const struct option options[] = { { "connections-max", required_argument, NULL, 'c' }, + { "exit-idle-time", required_argument, NULL, ARG_EXIT_IDLE }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, {} @@ -597,6 +650,12 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_EXIT_IDLE: + r = parse_sec(optarg, &arg_exit_idle_time); + if (r < 0) + return log_error_errno(r, "Failed to parse --exit-idle-time= argument: %s", optarg); + break; + case '?': return -EINVAL; diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index 52b9ce455..ca145aebf 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -238,17 +238,19 @@ static int run(int argc, char *argv[]) { ts = timespec_store(&_ts, t); } - { - struct pollfd p[3] = { - {.fd = fd, .events = events_a }, - {.fd = STDIN_FILENO, .events = events_b & POLLIN }, - {.fd = STDOUT_FILENO, .events = events_b & POLLOUT }, - }; + struct pollfd p[3] = { + { .fd = fd, .events = events_a }, + { .fd = STDIN_FILENO, .events = events_b & POLLIN }, + { .fd = STDOUT_FILENO, .events = events_b & POLLOUT }, + }; - r = ppoll(p, ELEMENTSOF(p), ts, NULL); - } + r = ppoll(p, ELEMENTSOF(p), ts, NULL); if (r < 0) return log_error_errno(errno, "ppoll() failed: %m"); + if (p[0].revents & POLLNVAL || + p[1].revents & POLLNVAL || + p[2].revents & POLLNVAL) + return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Invalid file descriptor to poll on?"); } return 0; diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index e92c402e8..5274cd24b 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -140,7 +140,7 @@ static int apply_all(OrderedHashmap *sysctl_options) { k = glob_extend(&paths, pattern, GLOB_NOCHECK); if (k < 0) { - if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) + if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k)) log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m", option->key); else { diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4a0aaad00..c58a19a09 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -20,10 +20,12 @@ #include "bootspec.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" #include "bus-message.h" +#include "bus-print-properties.h" #include "bus-unit-procs.h" #include "bus-unit-util.h" -#include "bus-util.h" #include "bus-wait-for-jobs.h" #include "bus-wait-for-units.h" #include "cgroup-show.h" @@ -130,6 +132,7 @@ static const char *arg_kill_who = NULL; static int arg_signal = SIGTERM; static char *arg_root = NULL; static usec_t arg_when = 0; +static const char *arg_reboot_argument = NULL; static enum action { ACTION_SYSTEMCTL, ACTION_HALT, @@ -321,28 +324,17 @@ static bool install_client_side(void) { } static int compare_unit_info(const UnitInfo *a, const UnitInfo *b) { - const char *d1, *d2; int r; /* First, order by machine */ - if (!a->machine && b->machine) - return -1; - if (a->machine && !b->machine) - return 1; - if (a->machine && b->machine) { - r = strcasecmp(a->machine, b->machine); - if (r != 0) - return r; - } + r = strcasecmp_ptr(a->machine, b->machine); + if (r != 0) + return r; /* Second, order by unit type */ - d1 = strrchr(a->id, '.'); - d2 = strrchr(b->id, '.'); - if (d1 && d2) { - r = strcasecmp(d1, d2); - if (r != 0) - return r; - } + r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.')); + if (r != 0) + return r; /* Third, order by name */ return strcasecmp(a->id, b->id); @@ -390,10 +382,23 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) { return true; } +static int output_table(Table *table) { + int r; + + assert(table); + + if (OUTPUT_MODE_IS_JSON(arg_output)) + r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO); + else + r = table_print(table, NULL); + if (r < 0) + return table_log_print_error(r); + + return 0; +} + static int output_units_list(const UnitInfo *unit_infos, unsigned c) { _cleanup_(table_unrefp) Table *table = NULL; - const UnitInfo *u; - int job_count = 0; int r; table = table_new("", "unit", "load", "active", "sub", "job", "description"); @@ -401,8 +406,8 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { return log_oom(); table_set_header(table, !arg_no_legend); - if (arg_no_legend) { - /* Hide the 'glyph' column when --no-legend is requested */ + if (arg_plain) { + /* Hide the 'glyph' column when --plain is requested */ r = table_hide_column_from_display(table, 0); if (r < 0) return log_error_errno(r, "Failed to hide column: %m"); @@ -410,7 +415,10 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { if (arg_full) table_set_width(table, 0); - for (u = unit_infos; unit_infos && u < unit_infos + c; u++) { + (void) table_set_empty_string(table, "-"); + + int job_count = 0; + for (const UnitInfo *u = unit_infos; unit_infos && u < unit_infos + c; u++) { _cleanup_free_ char *j = NULL; const char *on_underline = "", *on_loaded = "", *on_active = ""; const char *on_circle = "", *id; @@ -423,14 +431,15 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { } if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) { - on_circle = ansi_highlight_yellow(); + on_circle = underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow(); circle = true; on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); } else if (streq(u->active_state, "failed") && !arg_plain) { - on_circle = ansi_highlight_red(); + on_circle = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); circle = true; on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); } else { + on_circle = on_underline; on_active = on_underline; on_loaded = on_underline; } @@ -446,19 +455,19 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { r = table_add_many(table, TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", - TABLE_SET_COLOR, on_circle, + TABLE_SET_BOTH_COLORS, on_circle, TABLE_STRING, id, - TABLE_SET_COLOR, on_active, + TABLE_SET_BOTH_COLORS, on_active, TABLE_STRING, u->load_state, - TABLE_SET_COLOR, on_loaded, + TABLE_SET_BOTH_COLORS, on_loaded, TABLE_STRING, u->active_state, - TABLE_SET_COLOR, on_active, + TABLE_SET_BOTH_COLORS, on_active, TABLE_STRING, u->sub_state, - TABLE_SET_COLOR, on_active, + TABLE_SET_BOTH_COLORS, on_active, TABLE_STRING, u->job_id ? u->job_type: "", - TABLE_SET_COLOR, u->job_id ? on_underline : "", + TABLE_SET_BOTH_COLORS, u->job_id ? on_underline : "", TABLE_STRING, u->description, - TABLE_SET_COLOR, on_underline); + TABLE_SET_BOTH_COLORS, on_underline); if (r < 0) return table_log_add_error(r); @@ -473,9 +482,9 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { return log_error_errno(r, "Failed to hide column: %m"); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { const char *on, *off; @@ -529,13 +538,7 @@ static int get_unit_list( assert(unit_infos); assert(_reply); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitsByPatterns"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns"); if (r < 0) return bus_log_create_error(r); @@ -556,13 +559,7 @@ static int get_unit_list( m = sd_bus_message_unref(m); sd_bus_error_free(&error); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitsFiltered"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsFiltered"); if (r < 0) return bus_log_create_error(r); @@ -968,21 +965,15 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_ assert(a); assert(b); - if (!a->machine && b->machine) - return -1; - if (a->machine && !b->machine) - return 1; - if (a->machine && b->machine) { - r = strcasecmp(a->machine, b->machine); - if (r != 0) - return r; - } + r = strcasecmp_ptr(a->machine, b->machine); + if (r != 0) + return r; r = strcmp(a->path, b->path); - if (r == 0) - r = strcmp(a->type, b->type); + if (r != 0) + return r; - return r; + return strcmp(a->type, b->type); } static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { @@ -991,7 +982,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { const char *on, *off; int r; - table = table_new("listen", "type", "units", "activates"); + table = table_new("listen", "type", "unit", "activates"); if (!table) return log_oom(); @@ -1006,9 +997,11 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { if (arg_full) table_set_width(table, 0); + (void) table_set_empty_string(table, "-"); + if (cs) { for (s = socket_infos; s < socket_infos + cs; s++) { - _cleanup_free_ char *j = NULL, *activates = NULL; + _cleanup_free_ char *j = NULL; const char *path; if (s->machine) { @@ -1019,17 +1012,25 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { } else path = s->path; - activates = strv_join(s->triggered, ", "); - if (!activates) - return log_oom(); - r = table_add_many(table, TABLE_STRING, path, TABLE_STRING, s->type, - TABLE_STRING, s->id, - TABLE_STRING, activates); + TABLE_STRING, s->id); if (r < 0) return table_log_add_error(r); + + if (strv_isempty(s->triggered)) + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); + else if (strv_length(s->triggered) == 1) + r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]); + else + /* This should never happen, currently our socket units can only trigger a + * single unit. But let's handle this anyway, who knows what the future + * brings? */ + r = table_add_cell(table, NULL, TABLE_STRV, s->triggered); + if (r < 0) + return table_log_add_error(r); + } on = ansi_highlight(); @@ -1039,9 +1040,9 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { off = ansi_normal(); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n%s%u sockets listed.%s\n", on, cs, off); @@ -1217,15 +1218,9 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf assert(a); assert(b); - if (!a->machine && b->machine) - return -1; - if (a->machine && !b->machine) - return 1; - if (a->machine && b->machine) { - r = strcasecmp(a->machine, b->machine); - if (r != 0) - return r; - } + r = strcasecmp_ptr(a->machine, b->machine); + if (r != 0) + return r; r = CMP(a->next_elapse, b->next_elapse); if (r != 0) @@ -1250,6 +1245,8 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) { if (arg_full) table_set_width(table, 0); + (void) table_set_empty_string(table, "-"); + if (n > 0) { for (t = timer_infos; t < timer_infos + n; t++) { _cleanup_free_ char *j = NULL, *activates = NULL; @@ -1285,9 +1282,9 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) { off = ansi_normal(); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n%s%u timers listed.%s\n", on, n, off); @@ -1441,9 +1438,18 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p return true; } +static bool show_preset_for_state(UnitFileState state) { + /* Don't show preset state in those unit file states, it'll only confuse users. */ + return !IN_SET(state, + UNIT_FILE_ALIAS, + UNIT_FILE_STATIC, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT); +} + static int output_unit_file_list(const UnitFileList *units, unsigned c) { _cleanup_(table_unrefp) Table *table = NULL; - const UnitFileList *u; + _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; int r; table = table_new("unit file", "state", "vendor preset"); @@ -1454,9 +1460,10 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { if (arg_full) table_set_width(table, 0); - for (u = units; u < units + c; u++) { + (void) table_set_empty_string(table, "-"); + + for (const UnitFileList *u = units; u < units + c; u++) { const char *on_underline = NULL, *on_unit_color = NULL, *id; - const char *on_preset_color = NULL, *unit_preset_str; bool underline; underline = u + 1 < units + c && @@ -1471,39 +1478,52 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { UNIT_FILE_DISABLED, UNIT_FILE_BAD)) on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - else if (u->state == UNIT_FILE_ENABLED) + else if (IN_SET(u->state, + UNIT_FILE_ENABLED, + UNIT_FILE_ALIAS)) on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); else on_unit_color = on_underline; id = basename(u->path); - r = unit_file_query_preset(arg_scope, arg_root, id); - if (r < 0) { - unit_preset_str = "n/a"; - on_preset_color = underline ? on_underline : ansi_normal(); - } else if (r == 0) { - unit_preset_str = "disabled"; - on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); - } else { - unit_preset_str = "enabled"; - on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); - } - r = table_add_many(table, TABLE_STRING, id, - TABLE_SET_COLOR, strempty(on_underline), + TABLE_SET_BOTH_COLORS, strempty(on_underline), TABLE_STRING, unit_file_state_to_string(u->state), - TABLE_SET_COLOR, strempty(on_unit_color), - TABLE_STRING, unit_preset_str, - TABLE_SET_COLOR, strempty(on_preset_color)); + TABLE_SET_BOTH_COLORS, strempty(on_unit_color)); + if (r < 0) + return table_log_add_error(r); + + if (show_preset_for_state(u->state)) { + const char *unit_preset_str, *on_preset_color; + + r = unit_file_query_preset(arg_scope, arg_root, id, &presets); + if (r < 0) { + unit_preset_str = "n/a"; + on_preset_color = underline ? on_underline : ansi_normal(); + } else if (r == 0) { + unit_preset_str = "disabled"; + on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red(); + } else { + unit_preset_str = "enabled"; + on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green(); + } + + r = table_add_many(table, + TABLE_STRING, unit_preset_str, + TABLE_SET_BOTH_COLORS, strempty(on_preset_color)); + } else + r = table_add_many(table, + TABLE_EMPTY, + TABLE_SET_BOTH_COLORS, underline ? ansi_grey_underline() : ansi_grey()); if (r < 0) return table_log_add_error(r); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) printf("\n%u unit files listed.\n", c); @@ -1567,13 +1587,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitFilesByPatterns"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFilesByPatterns"); if (r < 0) return bus_log_create_error(r); @@ -1605,13 +1619,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { m = sd_bus_message_unref(m); sd_bus_error_free(&error); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitFiles"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFiles"); if (r < 0) return bus_log_create_error(r); @@ -1659,6 +1667,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { for (unit = units; unit < units + c; unit++) free(unit->path); + if (c == 0) + return -ENOENT; + return 0; } @@ -1984,8 +1995,8 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n) return log_oom(); table_set_header(table, !arg_no_legend); - if (arg_no_legend) { - /* Hide the 'glyph' column when --no-legend is requested */ + if (arg_plain) { + /* Hide the 'glyph' column when --plain is requested */ r = table_hide_column_from_display(table, 0); if (r < 0) return log_error_errno(r, "Failed to hide column: %m"); @@ -1993,6 +2004,8 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n) if (arg_full) table_set_width(table, 0); + (void) table_set_empty_string(table, "-"); + for (m = machine_infos; m < machine_infos + n; m++) { _cleanup_free_ char *mname = NULL; const char *on_state = "", *on_failed = ""; @@ -2030,9 +2043,9 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n) return table_log_add_error(r); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n"); @@ -2066,46 +2079,83 @@ static int list_machines(int argc, char *argv[], void *userdata) { return rc; } -static int get_default(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *_path = NULL; - const char *path; +static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { + char **ret = data; + + if (streq(key, "systemd.unit")) { + if (proc_cmdline_value_missing(key, value)) + return 0; + if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value); + + return free_and_strdup_warn(ret, key); + + } else if (!value) { + if (runlevel_to_target(key)) + return free_and_strdup_warn(ret, key); + } + + return 0; +} + +static void emit_cmdline_warning(void) { + if (arg_quiet || arg_root) + /* don't bother checking the commandline if we're operating on a container */ + return; + + _cleanup_free_ char *override = NULL; + int r; + + r = proc_cmdline_parse(parse_proc_cmdline_item, &override, 0); + if (r < 0) + log_debug_errno(r, "Failed to parse kernel command line, ignoring: %m"); + if (override) + log_notice("Note: found \"%s\" on the kernel commandline, which overrides the default unit.", + override); +} + +static int determine_default(char **ret_name) { int r; if (install_client_side()) { - r = unit_file_get_default(arg_scope, arg_root, &_path); + r = unit_file_get_default(arg_scope, arg_root, ret_name); if (r < 0) return log_error_errno(r, "Failed to get default target: %m"); - path = _path; + return 0; - r = 0; } else { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *name; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetDefaultTarget", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_systemd_mgr, "GetDefaultTarget", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to get default target: %s", bus_error_message(&error, r)); - r = sd_bus_message_read(reply, "s", &path); + r = sd_bus_message_read(reply, "s", &name); if (r < 0) return bus_log_parse_error(r); - } - if (path) - printf("%s\n", path); + return free_and_strdup_warn(ret_name, name); + } +} + +static int get_default(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *name = NULL; + int r; + + r = determine_default(&name); + if (r < 0) + return r; + + printf("%s\n", name); + + emit_cmdline_warning(); return 0; } @@ -2142,15 +2192,7 @@ static int set_default(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetDefaultTarget", - &error, - &reply, - "sb", unit, 1); + r = bus_call_method(bus, bus_systemd_mgr, "SetDefaultTarget", &error, &reply, "sb", unit, 1); if (r < 0) return log_error_errno(r, "Failed to set default target: %s", bus_error_message(&error, r)); @@ -2165,6 +2207,19 @@ static int set_default(int argc, char *argv[], void *userdata) { r = 0; } + emit_cmdline_warning(); + + if (!arg_quiet) { + _cleanup_free_ char *final = NULL; + + r = determine_default(&final); + if (r < 0) + return r; + + if (!streq(final, unit)) + log_notice("Note: \"%s\" is the default unit (possibly a runtime override).", final); + } + finish: unit_file_changes_free(changes, n_changes); @@ -2180,15 +2235,7 @@ static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const cha assert(bus); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - &error, - &reply, - "u", id); + r = bus_call_method(bus, bus_systemd_mgr, method, &error, &reply, "u", id); if (r < 0) return log_debug_errno(r, "Failed to get waiting jobs for job %" PRIu32, id); @@ -2255,13 +2302,14 @@ static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n if (arg_full) table_set_width(table, 0); + (void) table_set_empty_string(table, "-"); + for (j = jobs; j < jobs + n; j++) { if (streq(j->state, "running")) on = ansi_highlight(); else on = ""; - r = table_add_many(table, TABLE_UINT, j->id, TABLE_STRING, j->name, @@ -2312,15 +2360,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListJobs", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_systemd_mgr, "ListJobs", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to list jobs: %s", bus_error_message(&error, r)); @@ -2376,15 +2416,7 @@ static int cancel_job(int argc, char *argv[], void *userdata) { if (q < 0) return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name); - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CancelJob", - &error, - NULL, - "u", id); + q = bus_call_method(bus, bus_systemd_mgr, "CancelJob", &error, NULL, "u", id); if (q < 0) { log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q)); if (r == 0) @@ -2406,15 +2438,7 @@ static int need_daemon_reload(sd_bus *bus, const char *unit) { /* We don't use unit_dbus_path_from_name() directly since we * don't want to load the unit if it isn't loaded. */ - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit", - NULL, - &reply, - "s", unit); + r = bus_call_method(bus, bus_systemd_mgr, "GetUnit", NULL, &reply, "s", unit); if (r < 0) return r; @@ -2628,7 +2652,7 @@ static int unit_find_paths( if (ret_dropin_paths) { r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL, ".d", ".conf", - names, &dropins); + NULL, names, &dropins); if (r < 0) return r; } @@ -2838,11 +2862,9 @@ static int start_unit_one( _cleanup_(sd_bus_error_free) sd_bus_error enqueue_error = SD_BUS_ERROR_NULL; /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */ - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "EnqueueUnitJob", &enqueue_error, &reply, @@ -2888,15 +2910,7 @@ static int start_unit_one( } if (!done) { - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - error, - &reply, - "ss", name, mode); + r = bus_call_method(bus, bus_systemd_mgr, method, error, &reply, "ss", name, mode); if (r < 0) goto fail; @@ -3085,15 +3099,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (arg_wait) { - r = sd_bus_call_method_async( - bus, - NULL, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe", - NULL, NULL, - NULL); + r = bus_call_method_async(bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to enable subscription: %m"); @@ -3160,18 +3166,7 @@ static int logind_set_wall_message(void) { if (arg_dry_run) return 0; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetWallMessage", - &error, - NULL, - "sb", - m, - !arg_no_wall); - + r = bus_call_method(bus, bus_login_mgr, "SetWallMessage", &error, NULL, "sb", m, !arg_no_wall); if (r < 0) return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); return 0; @@ -3213,15 +3208,7 @@ static int logind_reboot(enum action a) { if (arg_dry_run) return 0; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - actions[a].method, - &error, - NULL, - "b", arg_ask_password); + r = bus_call_method(bus, bus_login_mgr, actions[a].method, &error, NULL, "b", arg_ask_password); if (r < 0) return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); @@ -3261,15 +3248,7 @@ static int logind_check_inhibitors(enum action a) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListInhibitors", - NULL, - &reply, - NULL); + r = bus_call_method(bus, bus_login_mgr, "ListInhibitors", NULL, &reply, NULL); if (r < 0) /* If logind is not around, then there are no inhibitors... */ return 0; @@ -3364,15 +3343,7 @@ static int prepare_firmware_setup(void) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetRebootToFirmwareSetup", - &error, - NULL, - "b", true); + r = bus_call_method(bus, bus_login_mgr, "SetRebootToFirmwareSetup", &error, NULL, "b", true); if (r < 0) return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r)); @@ -3397,15 +3368,7 @@ static int prepare_boot_loader_menu(void) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetRebootToBootLoaderMenu", - &error, - NULL, - "t", arg_boot_loader_menu); + r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderMenu", &error, NULL, "t", arg_boot_loader_menu); if (r < 0) return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r)); @@ -3430,15 +3393,7 @@ static int prepare_boot_loader_entry(void) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetRebootToBootLoaderEntry", - &error, - NULL, - "s", arg_boot_loader_entry); + r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderEntry", &error, NULL, "s", arg_boot_loader_entry); if (r < 0) return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r)); @@ -3541,15 +3496,7 @@ static int set_exit_code(uint8_t code) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetExitCode", - &error, - NULL, - "y", code); + r = bus_call_method(bus, bus_systemd_mgr, "SetExitCode", &error, NULL, "y", code); if (r < 0) return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r)); @@ -3588,10 +3535,23 @@ static int start_special(int argc, char *argv[], void *userdata) { if (r < 0) return r; - if (a == ACTION_REBOOT && argc > 1) { - r = update_reboot_parameter_and_warn(argv[1], false); - if (r < 0) - return r; + if (a == ACTION_REBOOT) { + const char *arg = NULL; + + if (argc > 1) { + if (arg_reboot_argument) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both --reboot-argument= and positional argument passed to reboot command, refusing."); + + log_notice("Positional argument to reboot command is deprecated, please use --reboot-argument= instead. Accepting anyway."); + arg = argv[1]; + } else + arg = arg_reboot_argument; + + if (arg) { + r = update_reboot_parameter_and_warn(arg, false); + if (r < 0) + return r; + } } else if (a == ACTION_KEXEC) { r = load_kexec_kernel(); @@ -3754,11 +3714,9 @@ static int kill_unit(int argc, char *argv[], void *userdata) { STRV_FOREACH(name, names) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - q = sd_bus_call_method( + q = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "KillUnit", &error, NULL, @@ -3773,11 +3731,12 @@ static int kill_unit(int argc, char *argv[], void *userdata) { return r; } -static int clean_unit(int argc, char *argv[], void *userdata) { +static int clean_or_freeze_unit(int argc, char *argv[], void *userdata) { _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL; _cleanup_strv_free_ char **names = NULL; int r, ret = EXIT_SUCCESS; char **name; + const char *method; sd_bus *bus; r = acquire_bus(BUS_FULL, &bus); @@ -3802,21 +3761,22 @@ static int clean_unit(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to allocate unit waiter: %m"); } + if (streq(argv[0], "clean")) + method = "CleanUnit"; + else if (streq(argv[0], "freeze")) + method = "FreezeUnit"; + else if (streq(argv[0], "thaw")) + method = "ThawUnit"; + else + assert_not_reached("Unhandled method"); + STRV_FOREACH(name, names) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; if (w) { /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */ - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "RefUnit", - &error, - NULL, - "s", *name); + r = bus_call_method(bus, bus_systemd_mgr, "RefUnit", &error, NULL, "s", *name); if (r < 0) { log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r)); if (ret == EXIT_SUCCESS) @@ -3825,13 +3785,7 @@ static int clean_unit(int argc, char *argv[], void *userdata) { } } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CleanUnit"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method); if (r < 0) return bus_log_create_error(r); @@ -3839,13 +3793,15 @@ static int clean_unit(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, arg_clean_what); - if (r < 0) - return bus_log_create_error(r); + if (streq(method, "CleanUnit")) { + r = sd_bus_message_append_strv(m, arg_clean_what); + if (r < 0) + return bus_log_create_error(r); + } r = sd_bus_call(bus, m, 0, &error, NULL); if (r < 0) { - log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r)); + log_error_errno(r, "Failed to %s unit %s: %s", argv[0], *name, bus_error_message(&error, r)); if (ret == EXIT_SUCCESS) { ret = r; continue; @@ -3985,6 +3941,7 @@ typedef struct UnitStatusInfo { const char *id; const char *load_state; const char *active_state; + const char *freezer_state; const char *sub_state; const char *unit_file_state; const char *unit_file_preset; @@ -4121,7 +4078,7 @@ static void print_status_info( bool *ellipsized) { char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; - const char *s1, *s2, *active_on, *active_off, *on, *off, *ss; + const char *s1, *s2, *active_on, *active_off, *on, *off, *ss, *fs; _cleanup_free_ char *formatted_path = NULL; ExecStatusInfo *p; usec_t timestamp; @@ -4159,14 +4116,18 @@ static void print_status_info( if (!isempty(i->load_error)) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); - else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset) && - !STR_IN_SET(i->unit_file_state, "generated", "transient")) - printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); - else if (path && !isempty(i->unit_file_state)) - printf(" Loaded: %s%s%s (%s; %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state); - else if (path) + else if (path && !isempty(i->unit_file_state)) { + bool show_preset = !isempty(i->unit_file_preset) && + show_preset_for_state(unit_file_state_from_string(i->unit_file_state)); + + printf(" Loaded: %s%s%s (%s; %s%s%s)\n", + on, strna(i->load_state), off, + path, + i->unit_file_state, + show_preset ? "; vendor preset: " : "", + show_preset ? i->unit_file_preset : ""); + + } else if (path) printf(" Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, path); else @@ -4221,6 +4182,10 @@ static void print_status_info( printf(" Active: %s%s%s", active_on, strna(i->active_state), active_off); + fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL; + if (fs) + printf(" %s(%s)%s", ansi_highlight_yellow(), fs, ansi_normal()); + if (!isempty(i->result) && !streq(i->result, "success")) printf(" (Result: %s)", i->result); @@ -4856,13 +4821,13 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m } else if (STR_IN_SET(name, "SystemCallFilter", "RestrictAddressFamilies")) { _cleanup_strv_free_ char **l = NULL; - int whitelist; + int allow_list; r = sd_bus_message_enter_container(m, 'r', "bas"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read(m, "b", &whitelist); + r = sd_bus_message_read(m, "b", &allow_list); if (r < 0) return bus_log_parse_error(r); @@ -4874,7 +4839,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m if (r < 0) return bus_log_parse_error(r); - if (all || whitelist || !strv_isempty(l)) { + if (all || allow_list || !strv_isempty(l)) { bool first = true; char **i; @@ -4883,7 +4848,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m fputc('=', stdout); } - if (!whitelist) + if (!allow_list) fputc('~', stdout); STRV_FOREACH(i, l) { @@ -5225,7 +5190,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return 1; - } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) { + } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "StandardInputData", "RootHashSignature")) { _cleanup_free_ char *h = NULL; const void *p; size_t sz; @@ -5478,12 +5443,14 @@ static int show_one( static const struct bus_properties_map property_map[] = { { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) }, { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) }, + { "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) }, { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) }, {} }, status_map[] = { { "Id", "s", NULL, offsetof(UnitStatusInfo, id) }, { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) }, { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) }, + { "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) }, { "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) }, { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) }, { "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) }, @@ -5656,15 +5623,7 @@ static int get_unit_dbus_path_by_pid( char *u; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitByPID", - &error, - &reply, - "u", pid); + r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", pid); if (r < 0) return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r)); @@ -5993,13 +5952,7 @@ static int set_property(int argc, char *argv[], void *userdata) { polkit_agent_open_maybe(); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetUnitProperties"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetUnitProperties"); if (r < 0) return bus_log_create_error(r); @@ -6066,13 +6019,7 @@ static int daemon_reload(int argc, char *argv[], void *userdata) { assert_not_reached("Unexpected action"); } - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method); if (r < 0) return bus_log_create_error(r); @@ -6120,15 +6067,7 @@ static int trivial_method(int argc, char *argv[], void *userdata) { streq(argv[0], "exit") ? "Exit" : /* poweroff */ "PowerOff"; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - &error, - NULL, - NULL); + r = bus_call_method(bus, bus_systemd_mgr, method, &error, NULL, NULL); if (r < 0 && arg_action == ACTION_SYSTEMCTL) return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); @@ -6160,15 +6099,7 @@ static int reset_failed(int argc, char *argv[], void *userdata) { STRV_FOREACH(name, names) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ResetFailedUnit", - &error, - NULL, - "s", *name); + q = bus_call_method(bus, bus_systemd_mgr, "ResetFailedUnit", &error, NULL, "s", *name); if (q < 0) { log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q)); if (r == 0) @@ -6209,15 +6140,7 @@ static int show_environment(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Environment", - &error, - &reply, - "as"); + r = bus_get_property(bus, bus_systemd_mgr, "Environment", &error, &reply, "as"); if (r < 0) return log_error_errno(r, "Failed to get environment: %s", bus_error_message(&error, r)); @@ -6298,15 +6221,7 @@ static int switch_root(int argc, char *argv[], void *userdata) { log_debug("Switching root - root: %s; init: %s", root, strna(init)); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SwitchRoot", - &error, - NULL, - "ss", root, init); + r = bus_call_method(bus, bus_systemd_mgr, "SwitchRoot", &error, NULL, "ss", root, init); if (r < 0) { (void) default_signals(SIGTERM, -1); @@ -6328,14 +6243,7 @@ static int log_level(int argc, char *argv[], void *userdata) { if (argc == 1) { _cleanup_free_ char *level = NULL; - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogLevel", - &error, - &level); + r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level); if (r < 0) return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r)); @@ -6344,15 +6252,7 @@ static int log_level(int argc, char *argv[], void *userdata) { } else { assert(argc == 2); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogLevel", - &error, - "s", - argv[1]); + r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]); if (r < 0) return log_error_errno(r, "Failed to set log level: %s", bus_error_message(&error, r)); } @@ -6372,14 +6272,7 @@ static int log_target(int argc, char *argv[], void *userdata) { if (argc == 1) { _cleanup_free_ char *target = NULL; - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogTarget", - &error, - &target); + r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target); if (r < 0) return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r)); @@ -6388,15 +6281,7 @@ static int log_target(int argc, char *argv[], void *userdata) { } else { assert(argc == 2); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LogTarget", - &error, - "s", - argv[1]); + r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]); if (r < 0) return log_error_errno(r, "Failed to set log target: %s", bus_error_message(&error, r)); } @@ -6417,15 +6302,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) { if (argc == 1) { /* get ServiceWatchdogs */ - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ServiceWatchdogs", - &error, - 'b', - &b); + 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)); @@ -6439,15 +6316,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) { if (b < 0) return log_error_errno(b, "Failed to parse service-watchdogs argument: %m"); - r = sd_bus_set_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ServiceWatchdogs", - &error, - "b", - b); + 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)); } @@ -6475,13 +6344,7 @@ static int set_environment(int argc, char *argv[], void *userdata) { ? "SetEnvironment" : "UnsetEnvironment"; - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method); if (r < 0) return bus_log_create_error(r); @@ -6508,13 +6371,7 @@ static int import_environment(int argc, char *argv[], void *userdata) { polkit_agent_open_maybe(); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetEnvironment"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetEnvironment"); if (r < 0) return bus_log_create_error(r); @@ -6953,13 +6810,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { } else assert_not_reached("Unknown verb"); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method); if (r < 0) return bus_log_create_error(r); @@ -7095,13 +6946,7 @@ static int add_dependency(int argc, char *argv[], void *userdata) { polkit_agent_open_maybe(); - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "AddDependencyUnitFiles"); + r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "AddDependencyUnitFiles"); if (r < 0) return bus_log_create_error(r); @@ -7157,11 +7002,9 @@ static int preset_all(int argc, char *argv[], void *userdata) { polkit_agent_open_maybe(); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "PresetAllUnitFiles", &error, &reply, @@ -7218,15 +7061,7 @@ static int show_installation_targets(sd_bus *bus, const char *name) { const char *link; int r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileLinks", - &error, - &reply, - "sb", name, arg_runtime); + r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileLinks", &error, &reply, "sb", name, arg_runtime); if (r < 0) return log_error_errno(r, "Failed to get unit file links for %s: %s", name, bus_error_message(&error, r)); @@ -7275,6 +7110,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_STATIC, + UNIT_FILE_ALIAS, UNIT_FILE_INDIRECT, UNIT_FILE_GENERATED)) enabled = true; @@ -7302,15 +7138,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *s; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileState", - &error, - &reply, - "s", *name); + r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileState", &error, &reply, "s", *name); if (r < 0) return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r)); @@ -7341,14 +7169,7 @@ static int match_startup_finished(sd_bus_message *m, void *userdata, sd_bus_erro assert(state); - r = sd_bus_get_property_string( - sd_bus_message_get_bus(m), - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SystemState", - NULL, - state); + r = bus_get_property_string(sd_bus_message_get_bus(m), bus_systemd_mgr, "SystemState", NULL, state); sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), r); return 0; @@ -7377,12 +7198,10 @@ static int is_system_running(int argc, char *argv[], void *userdata) { if (r >= 0) r = sd_bus_attach_event(bus, event, 0); if (r >= 0) - r = sd_bus_match_signal_async( + r = bus_match_signal_async( bus, &slot_startup_finished, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "StartupFinished", match_startup_finished, NULL, &state); if (r < 0) { @@ -7391,14 +7210,7 @@ static int is_system_running(int argc, char *argv[], void *userdata) { } } - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SystemState", - &error, - &state); + r = bus_get_property_string(bus, bus_systemd_mgr, "SystemState", &error, &state); if (r < 0) { log_warning_errno(r, "Failed to query system state: %s", bus_error_message(&error, r)); @@ -7827,6 +7639,8 @@ static int systemctl_help(void) { " kill UNIT... Send signal to processes of a unit\n" " clean UNIT... Clean runtime, cache, state, logs or\n" " configuration of unit\n" + " freeze PATTERN... Freeze execution of unit processes\n" + " thaw PATTERN... Resume execution of a frozen unit\n" " is-active PATTERN... Check whether units are active\n" " is-failed PATTERN... Check whether units are failed\n" " status [PATTERN...|PID...] Show runtime status of one or more units\n" @@ -7886,7 +7700,7 @@ static int systemctl_help(void) { " emergency Enter system emergency mode\n" " halt Shut down and halt the system\n" " poweroff Shut down and power-off the system\n" - " reboot [ARG] Shut down and reboot the system\n" + " reboot Shut down and reboot the system\n" " kexec Shut down and reboot the system with kexec\n" " exit [EXIT_CODE] Request user instance or container exit\n" " switch-root ROOT [INIT] Change to a different root file system\n" @@ -7906,6 +7720,7 @@ static int systemctl_help(void) { " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n" " --failed Shorcut for --state=failed\n" " -p --property=NAME Show only properties by this name\n" + " -P NAME Equivalent to --value --property=NAME\n" " -a --all Show all properties/all units currently in memory,\n" " including dead/empty ones. To list all units installed\n" " on the system, use 'list-unit-files' instead.\n" @@ -8153,14 +7968,7 @@ static int help_boot_loader_entry(void) { if (r < 0) return r; - r = sd_bus_get_property_strv( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "BootLoaderEntries", - &error, - &l); + r = bus_get_property_strv(bus, bus_login_mgr, "BootLoaderEntries", &error, &l); if (r < 0) return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r)); @@ -8210,6 +8018,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_WITH_DEPENDENCIES, ARG_WAIT, ARG_WHAT, + ARG_REBOOT_ARG, }; static const struct option options[] = { @@ -8263,6 +8072,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "message", required_argument, NULL, ARG_MESSAGE }, { "show-transaction", no_argument, NULL, 'T' }, { "what", required_argument, NULL, ARG_WHAT }, + { "reboot-argument", required_argument, NULL, ARG_REBOOT_ARG }, {} }; @@ -8274,7 +8084,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ arg_ask_password = true; - while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ht:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0) switch (c) { @@ -8329,6 +8139,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; } + case 'P': + arg_value = true; + _fallthrough_; + case 'p': /* Make sure that if the empty property list was specified, we won't show any properties. */ @@ -8353,9 +8167,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } } - /* If the user asked for a particular - * property, show it to him, even if it is - * empty. */ + /* If the user asked for a particular property, show it, even if it is empty. */ arg_all = true; break; @@ -8519,6 +8331,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output '%s'.", optarg); + + if (OUTPUT_MODE_IS_JSON(arg_output)) { + arg_no_legend = true; + arg_plain = true; + } break; case 'i': @@ -8651,6 +8468,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; } + case ARG_REBOOT_ARG: + arg_reboot_argument = optarg; + break; + case '.': /* Output an error mimicking getopt, and print a hint afterwards */ log_error("%s: invalid option -- '.'", program_invocation_name); @@ -9097,7 +8918,9 @@ static int systemctl_main(int argc, char *argv[]) { { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */ { "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit }, { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit }, - { "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_unit }, + { "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 }, @@ -9107,9 +8930,9 @@ static int systemctl_main(int argc, char *argv[]) { { "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, 0, log_level }, - { "log-target", VERB_ANY, 2, 0, log_target }, - { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs }, + { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_level }, + { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_target }, + { "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 }, @@ -9149,6 +8972,12 @@ static int systemctl_main(int argc, char *argv[]) { {} }; + const Verb *verb = verbs_find_verb(argv[optind], verbs); + if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Verb '%s' cannot be used with --root=.", + argv[optind] ?: verb->verb); + return dispatch_verb(argc, argv, verbs, NULL); } @@ -9263,17 +9092,7 @@ static int logind_schedule_shutdown(void) { (void) logind_set_wall_message(); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ScheduleShutdown", - &error, - NULL, - "st", - action, - arg_when); + 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)); @@ -9375,14 +9194,7 @@ static int logind_cancel_shutdown(void) { (void) logind_set_wall_message(); - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CancelScheduledShutdown", - &error, - NULL, NULL); + r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL); if (r < 0) return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r)); @@ -9397,7 +9209,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_parse_environment(); + log_parse_environment_cli(); log_open(); /* The journal merging logic potentially needs a lot of fds. */ diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 05196554a..62baf7784 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -12,6 +12,7 @@ _systemd_headers = ''' sd-journal.h sd-login.h sd-messages.h + sd-path.h '''.split() # https://github.com/mesonbuild/meson/issues/1633 @@ -23,6 +24,7 @@ _not_installed_headers = ''' sd-dhcp-client.h sd-dhcp-lease.h sd-dhcp-option.h + sd-dhcp6-option.h sd-dhcp-server.h sd-ipv4acd.h sd-ipv4ll.h @@ -30,7 +32,6 @@ _not_installed_headers = ''' sd-ndisc.h sd-netlink.h sd-network.h - sd-path.h sd-radv.h sd-resolve.h sd-utf8.h diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index 95b591423..b10a3e04b 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -44,6 +44,7 @@ enum { SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION = 1ULL << 6, SD_BUS_VTABLE_PROPERTY_EXPLICIT = 1ULL << 7, SD_BUS_VTABLE_SENSITIVE = 1ULL << 8, /* covers both directions: method call + reply */ + SD_BUS_VTABLE_ABSOLUTE_OFFSET = 1ULL << 9, _SD_BUS_VTABLE_CAPABILITY_MASK = 0xFFFFULL << 40 }; @@ -187,6 +188,124 @@ struct sd_bus_vtable { .x = { { 0 } }, \ } +#define _SD_ECHO(X) X +#define _SD_CONCAT(X) #X "\0" + +#define _SD_VARARGS_FOREACH_EVEN_01(FN, X, ...) +#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_01(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_03(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_03(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_05(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_04(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_06(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_05(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_07(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_06(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_08(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_07(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_09(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_08(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_10(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_09(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_11(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_10(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_12(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_11(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_13(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_12(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_14(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_13(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_15(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_14(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_16(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_15(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_17(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_16(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_18(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_17(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_19(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_18(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_20(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_19(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_21(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_20(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_22(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_21(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_23(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_22(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_24(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_23(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_25(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_24(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_26(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_25(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_27(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_26(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_28(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_27(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_29(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_28(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_30(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_29(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_31(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_30(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_32(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_31(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_33(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_32(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_34(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_33(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_35(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_34(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_36(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_35(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_37(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_36(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_38(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_37(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_39(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_38(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_40(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_39(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_41(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_40(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_42(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_41(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_43(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_42(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_44(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_43(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_45(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_44(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_46(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_45(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_47(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_46(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_48(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_47(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_49(FN, X, ...) _SD_VARARGS_FOREACH_EVEN_48(FN, __VA_ARGS__) +#define _SD_VARARGS_FOREACH_EVEN_50(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_49(FN, __VA_ARGS__) + +#define _SD_VARARGS_FOREACH_EVEN_SEQ(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + NAME, ...) NAME + +#define _SD_VARARGS_FOREACH_EVEN(FN, ...) \ + _SD_VARARGS_FOREACH_EVEN_SEQ(__VA_ARGS__, \ + _SD_VARARGS_FOREACH_EVEN_50, _SD_VARARGS_FOREACH_EVEN_49, \ + _SD_VARARGS_FOREACH_EVEN_48, _SD_VARARGS_FOREACH_EVEN_47, \ + _SD_VARARGS_FOREACH_EVEN_46, _SD_VARARGS_FOREACH_EVEN_45, \ + _SD_VARARGS_FOREACH_EVEN_44, _SD_VARARGS_FOREACH_EVEN_43, \ + _SD_VARARGS_FOREACH_EVEN_42, _SD_VARARGS_FOREACH_EVEN_41, \ + _SD_VARARGS_FOREACH_EVEN_40, _SD_VARARGS_FOREACH_EVEN_39, \ + _SD_VARARGS_FOREACH_EVEN_38, _SD_VARARGS_FOREACH_EVEN_37, \ + _SD_VARARGS_FOREACH_EVEN_36, _SD_VARARGS_FOREACH_EVEN_35, \ + _SD_VARARGS_FOREACH_EVEN_34, _SD_VARARGS_FOREACH_EVEN_33, \ + _SD_VARARGS_FOREACH_EVEN_32, _SD_VARARGS_FOREACH_EVEN_31, \ + _SD_VARARGS_FOREACH_EVEN_30, _SD_VARARGS_FOREACH_EVEN_29, \ + _SD_VARARGS_FOREACH_EVEN_28, _SD_VARARGS_FOREACH_EVEN_27, \ + _SD_VARARGS_FOREACH_EVEN_26, _SD_VARARGS_FOREACH_EVEN_25, \ + _SD_VARARGS_FOREACH_EVEN_24, _SD_VARARGS_FOREACH_EVEN_23, \ + _SD_VARARGS_FOREACH_EVEN_22, _SD_VARARGS_FOREACH_EVEN_21, \ + _SD_VARARGS_FOREACH_EVEN_20, _SD_VARARGS_FOREACH_EVEN_19, \ + _SD_VARARGS_FOREACH_EVEN_18, _SD_VARARGS_FOREACH_EVEN_17, \ + _SD_VARARGS_FOREACH_EVEN_16, _SD_VARARGS_FOREACH_EVEN_15, \ + _SD_VARARGS_FOREACH_EVEN_14, _SD_VARARGS_FOREACH_EVEN_13, \ + _SD_VARARGS_FOREACH_EVEN_12, _SD_VARARGS_FOREACH_EVEN_11, \ + _SD_VARARGS_FOREACH_EVEN_10, _SD_VARARGS_FOREACH_EVEN_09, \ + _SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \ + _SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \ + _SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \ + _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \ + (FN, __VA_ARGS__) + +#define SD_BUS_ARGS(...) __VA_ARGS__ +#define SD_BUS_RESULT(...) __VA_ARGS__ + +#define SD_BUS_NO_ARGS SD_BUS_ARGS(NULL,) +#define SD_BUS_NO_RESULT SD_BUS_RESULT(NULL,) + +#define SD_BUS_METHOD_WITH_ARGS(_member, _args, _result, _handler, _flags) \ + SD_BUS_METHOD_WITH_NAMES(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""), \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result), \ + _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _result, ""), \ + _handler, _flags) + +#define SD_BUS_METHOD_WITH_ARGS_OFFSET(_member, _args, _result, _handler, _offset, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""), \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result), \ + _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _result, ""), \ + _handler, _offset, _flags) + +#define SD_BUS_SIGNAL_WITH_ARGS(_member, _args, _flags) \ + SD_BUS_SIGNAL_WITH_NAMES(_member, \ + _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args), \ + _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""), \ + _flags) + _SD_END_DECLARATIONS; #endif diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index e6f329874..d4b6befc8 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -55,7 +55,7 @@ typedef struct { } sd_bus_error; typedef struct { - const char* name; + const char *name; int code; } sd_bus_error_map; @@ -124,6 +124,13 @@ typedef _sd_destroy_t sd_bus_destroy_t; #include "sd-bus-protocol.h" #include "sd-bus-vtable.h" +/* Naming */ + +int sd_bus_interface_name_is_valid(const char *p); +int sd_bus_service_name_is_valid(const char *p); +int sd_bus_member_name_is_valid(const char *p); +int sd_bus_object_path_is_valid(const char *p); + /* Connections */ int sd_bus_default(sd_bus **ret); @@ -143,7 +150,7 @@ int sd_bus_new(sd_bus **ret); int sd_bus_set_address(sd_bus *bus, const char *address); int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); -int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]); +int sd_bus_set_exec(sd_bus *bus, const char *path, char *const *argv); int sd_bus_get_address(sd_bus *bus, const char **address); int sd_bus_set_bus_client(sd_bus *bus, int b); int sd_bus_is_bus_client(sd_bus *bus); @@ -177,13 +184,13 @@ int sd_bus_get_sender(sd_bus *bus, const char **ret); int sd_bus_start(sd_bus *bus); -int sd_bus_try_close(sd_bus *bus); +int sd_bus_try_close(sd_bus *bus) _sd_deprecated_; void sd_bus_close(sd_bus *bus); -sd_bus *sd_bus_ref(sd_bus *bus); -sd_bus *sd_bus_unref(sd_bus *bus); -sd_bus *sd_bus_close_unref(sd_bus *bus); -sd_bus *sd_bus_flush_close_unref(sd_bus *bus); +sd_bus* sd_bus_ref(sd_bus *bus); +sd_bus* sd_bus_unref(sd_bus *bus); +sd_bus* sd_bus_close_unref(sd_bus *bus); +sd_bus* sd_bus_flush_close_unref(sd_bus *bus); void sd_bus_default_flush_close(void); @@ -204,7 +211,7 @@ int sd_bus_get_fd(sd_bus *bus); int sd_bus_get_events(sd_bus *bus); int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec); int sd_bus_process(sd_bus *bus, sd_bus_message **r); -int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); +int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r) _sd_deprecated_; int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); int sd_bus_flush(sd_bus *bus); int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m); @@ -216,7 +223,7 @@ void* sd_bus_get_current_userdata(sd_bus *bus); int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); int sd_bus_detach_event(sd_bus *bus); -sd_event *sd_bus_get_event(sd_bus *bus); +sd_event* sd_bus_get_event(sd_bus *bus); int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret); int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret); @@ -240,8 +247,8 @@ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); -void *sd_bus_slot_get_userdata(sd_bus_slot *slot); -void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); +void* sd_bus_slot_get_userdata(sd_bus_slot *slot); +void* sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description); int sd_bus_slot_get_floating(sd_bus_slot *slot); @@ -251,7 +258,7 @@ int sd_bus_slot_get_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t *callback) sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot); -void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot); +void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot); /* Message object */ @@ -272,27 +279,27 @@ int sd_bus_message_seal(sd_bus_message *m, uint64_t cookie, uint64_t timeout_use int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie); int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie); -int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority); +int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) _sd_deprecated_; int sd_bus_message_get_expect_reply(sd_bus_message *m); int sd_bus_message_get_auto_start(sd_bus_message *m); int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m); -const char *sd_bus_message_get_signature(sd_bus_message *m, int complete); -const char *sd_bus_message_get_path(sd_bus_message *m); -const char *sd_bus_message_get_interface(sd_bus_message *m); -const char *sd_bus_message_get_member(sd_bus_message *m); -const char *sd_bus_message_get_destination(sd_bus_message *m); -const char *sd_bus_message_get_sender(sd_bus_message *m); -const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m); +const char* sd_bus_message_get_signature(sd_bus_message *m, int complete); +const char* sd_bus_message_get_path(sd_bus_message *m); +const char* sd_bus_message_get_interface(sd_bus_message *m); +const char* sd_bus_message_get_member(sd_bus_message *m); +const char* sd_bus_message_get_destination(sd_bus_message *m); +const char* sd_bus_message_get_sender(sd_bus_message *m); +const sd_bus_error* sd_bus_message_get_error(sd_bus_message *m); int sd_bus_message_get_errno(sd_bus_message *m); int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec); int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec); -int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t* seqnum); +int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum); sd_bus* sd_bus_message_get_bus(sd_bus_message *m); -sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ +sd_bus_creds* sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); @@ -306,7 +313,7 @@ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b) int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); int sd_bus_message_set_sender(sd_bus_message *m, const char *sender); -int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); +int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) _sd_deprecated_; int sd_bus_message_append(sd_bus_message *m, const char *types, ...); int sd_bus_message_appendv(sd_bus_message *m, const char *types, va_list ap); @@ -352,20 +359,27 @@ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machin /* Convenience calls */ +int sd_bus_call_methodv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, va_list ap); int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...); +int sd_bus_call_method_asyncv(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, va_list ap); int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type); int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr); int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */ int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */ +int sd_bus_set_propertyv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, va_list ap); int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...); +int sd_bus_reply_method_returnv(sd_bus_message *call, const char *types, va_list ap); int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...); int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e); +int sd_bus_reply_method_errorfv(sd_bus_message *call, const char *name, const char *format, va_list ap) _sd_printf_(3, 0); int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e); +int sd_bus_reply_method_errnofv(sd_bus_message *call, int error, const char *format, va_list ap) _sd_printf_(3, 0); int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_emit_signalv(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, va_list ap); int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); @@ -378,8 +392,8 @@ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *inte int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; -int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); -int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); +int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **creds); +int sd_bus_query_sender_privilege(sd_bus_message *m, int capability); int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata); int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t match_callback, sd_bus_message_handler_t add_callback, void *userdata); @@ -387,8 +401,8 @@ int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender /* Credential handling */ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); -sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c); -sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c); +sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c); +sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c); uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c); @@ -486,8 +500,8 @@ sd_bus_track* sd_bus_track_ref(sd_bus_track *track); sd_bus_track* sd_bus_track_unref(sd_bus_track *track); sd_bus* sd_bus_track_get_bus(sd_bus_track *track); -void *sd_bus_track_get_userdata(sd_bus_track *track); -void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); +void* sd_bus_track_get_userdata(sd_bus_track *track); +void* sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m); int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m); diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h index 62b0f723c..b47b15a44 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -286,6 +286,19 @@ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _s */ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); +/* + Returns > 0 if synchronization with systemd succeeded. Returns < 0 + on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the + timeout parameter of this function call takes the timeout in µs, and + will be passed to ppoll(2), hence the behaviour will be similar to + ppoll(2). This function can be called after sending a status message + to systemd, if one needs to synchronize against reception of the + status messages sent before this call is made. Therefore, this + cannot be used to know if the status message was processed + successfully, but to only synchronize against its consumption. +*/ +int sd_notify_barrier(int unset_environment, uint64_t timeout); + /* Returns > 0 if the system was booted with systemd. Returns < 0 on error. Returns 0 if the system was not booted with systemd. Note diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 0002ea0e5..ac3b5b369 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -48,6 +48,7 @@ enum { 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, @@ -83,6 +84,8 @@ enum { 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_NEW_POSIX_TIMEZONE = 100, @@ -90,6 +93,7 @@ enum { 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_PRIVATE_BASE = 224, /* Windows 10 option to send when Anonymize=true */ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, @@ -169,6 +173,9 @@ int sd_dhcp_client_set_hostname( int sd_dhcp_client_set_vendor_class_identifier( sd_dhcp_client *client, const char *vci); +int sd_dhcp_client_set_mud_url( + sd_dhcp_client *client, + const char *mudurl); int sd_dhcp_client_set_user_class( sd_dhcp_client *client, const char* const *user_class); @@ -178,8 +185,12 @@ int sd_dhcp_client_get_lease( int sd_dhcp_client_set_service_type( sd_dhcp_client *client, int type); +int sd_dhcp_client_set_fallback_lease_lifetime( + sd_dhcp_client *client, + uint32_t fallback_lease_lifetime); -int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v); +int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v); +int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); @@ -194,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); * options when using RFC7844 Anonymity Profiles */ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize); +int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret); + int sd_dhcp_client_attach_event( sd_dhcp_client *client, sd_event *event, diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index b80d607fe..17bd49181 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -33,6 +33,17 @@ typedef struct sd_dhcp_route sd_dhcp_route; sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease); sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); +typedef enum sd_dhcp_lease_server_type { + SD_DHCP_LEASE_DNS, + SD_DHCP_LEASE_NTP, + SD_DHCP_LEASE_SIP, + SD_DHCP_LEASE_POP3, + SD_DHCP_LEASE_SMTP, + SD_DHCP_LEASE_LPR, + _SD_DHCP_LEASE_SERVER_TYPE_MAX, + _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1, +} sd_dhcp_lease_server_type; + int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); @@ -42,9 +53,13 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr); int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); 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); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 5950506c9..b6a5e9db8 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -21,6 +21,7 @@ #include #include +#include "sd-dhcp-lease.h" #include "sd-dhcp-option.h" #include "sd-event.h" @@ -30,6 +31,10 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp_server sd_dhcp_server; +enum { + SD_DHCP_SERVER_EVENT_LEASE_CHANGED = 1 << 0, +}; + int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex); sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); @@ -39,20 +44,35 @@ int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t int sd_dhcp_server_detach_event(sd_dhcp_server *client); sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); +typedef void (*sd_dhcp_server_callback_t)(sd_dhcp_server *server, uint64_t event, void *userdata); + +int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata); + int sd_dhcp_server_is_running(sd_dhcp_server *server); int sd_dhcp_server_start(sd_dhcp_server *server); int sd_dhcp_server_stop(sd_dhcp_server *server); -int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); +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_timezone(sd_dhcp_server *server, const char *timezone); -int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); -int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); -int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n); int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); +int sd_dhcp_server_set_servers( + sd_dhcp_server *server, + sd_dhcp_lease_server_type what, + const struct in_addr addresses[], + size_t n_addresses); + +int sd_dhcp_server_set_lpr(sd_dhcp_server *server, const struct in_addr lpr[], size_t n); +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], size_t n); +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], size_t n); +int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], size_t n); +int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n); +int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n); + int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v); +int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index be34d43e7..2b0d63a52 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -24,6 +24,7 @@ #include #include "sd-dhcp6-lease.h" +#include "sd-dhcp6-option.h" #include "sd-event.h" #include "_sd-common.h" @@ -73,6 +74,7 @@ enum { SD_DHCP6_OPTION_FQDN = 39, /* RFC 4704 */ SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ + SD_DHCP6_OPTION_MUD_URL = 112, /* RFC 8250 */ /* option codes 89-142 are unassigned */ /* option codes 144-65535 are unassigned */ @@ -108,6 +110,12 @@ int sd_dhcp6_client_set_duid_llt( int sd_dhcp6_client_set_iaid( sd_dhcp6_client *client, uint32_t iaid); +int sd_dhcp6_client_get_iaid( + sd_dhcp6_client *client, + uint32_t *iaid); +int sd_dhcp6_client_duid_as_string( + sd_dhcp6_client *client, + char **duid); int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn); @@ -120,6 +128,15 @@ int sd_dhcp6_client_get_information_request( 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); +int sd_dhcp6_client_set_request_user_class( + sd_dhcp6_client *client, + char** user_class); +int sd_dhcp6_client_set_request_vendor_class( + sd_dhcp6_client *client, + char** vendor_class); int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, @@ -132,12 +149,17 @@ 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_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); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, sd_dhcp6_lease **ret); +int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v); + int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); int sd_dhcp6_client_is_running(sd_dhcp6_client *client); diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 33a32a6dc..4301c6db8 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -39,10 +39,9 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); diff --git a/src/systemd/sd-dhcp6-option.h b/src/systemd/sd-dhcp6-option.h new file mode 100644 index 000000000..88a498631 --- /dev/null +++ b/src/systemd/sd-dhcp6-option.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#ifndef foosddhcp6optionhfoo +#define foosddhcp6optionhfoo + +/*** + 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 + Lesser 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 . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp6_option sd_dhcp6_option; + +int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret); +sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra); +sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 29ff40ab2..d220f21aa 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -72,7 +72,7 @@ enum { SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */ - SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */ + SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* old name */ }; /* Wakeup event types */ @@ -88,7 +88,7 @@ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */ +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); @@ -105,6 +105,7 @@ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz); int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); +int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *l); void sd_journal_restart_data(sd_journal *j); int sd_journal_add_match(sd_journal *j, const void *data, size_t size); @@ -128,6 +129,7 @@ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes); int sd_journal_query_unique(sd_journal *j, const char *field); int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); +int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *l); void sd_journal_restart_unique(sd_journal *j); int sd_journal_enumerate_fields(sd_journal *j, const char **field); @@ -156,13 +158,13 @@ int sd_journal_has_persistent_files(sd_journal *j); if (sd_journal_seek_tail(j) < 0) { } \ else while (sd_journal_previous(j) > 0) -/* Iterate through the data fields of the current journal entry */ +/* Iterate through all available data fields of the current journal entry */ #define SD_JOURNAL_FOREACH_DATA(j, data, l) \ - for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + for (sd_journal_restart_data(j); sd_journal_enumerate_available_data((j), &(data), &(l)) > 0; ) -/* Iterate through the all known values of a specific field */ +/* Iterate through all available values of a specific field */ #define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ - for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; ) + for (sd_journal_restart_unique(j); sd_journal_enumerate_available_unique((j), &(data), &(l)) > 0; ) /* Iterate through all known field names */ #define SD_JOURNAL_FOREACH_FIELD(j, field) \ diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index bf3afadce..c2abc2012 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -96,6 +96,9 @@ enum { #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E } +#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01 + /* IEEE 802.1AB-2009 Annex E */ enum { SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, @@ -169,10 +172,11 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); -/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs +/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index 50be5433d..e18f01bb6 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -183,7 +183,7 @@ int sd_seat_get_active(const char *seat, char **session, uid_t *uid); int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); /* Return whether the seat is multi-session capable */ -int sd_seat_can_multi_session(const char *seat); +int sd_seat_can_multi_session(const char *seat) _sd_deprecated_; /* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */ int sd_seat_can_tty(const char *seat); diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 162b650e6..a4e9680dc 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -161,6 +161,20 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) #define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) +#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE \ + SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93) +#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR \ + SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93) + +#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c) +#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR \ + SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c) + +#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED \ + SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33) +#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) + _SD_END_DECLARATIONS; #endif diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index d1bee343a..3ddfc8cb6 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -30,7 +30,7 @@ _SD_BEGIN_DECLARATIONS; -/* Neightbor Discovery Options, RFC 4861, Section 4.6 and +/* Neighbor Discovery Options, RFC 4861, Section 4.6 and * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */ enum { SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 644d462b6..f9196491d 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -86,6 +86,10 @@ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uin int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data); int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data); int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data); +int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data); +int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data); +int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data); +int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data); int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len); int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data); int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data); @@ -208,6 +212,10 @@ int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16 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); + /* genl */ int sd_genl_socket_open(sd_netlink **nl); int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index d0b432274..42bcd74b7 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -110,14 +110,14 @@ int sd_network_link_get_network_file(int ifindex, char **filename); * IP addresses */ int sd_network_link_get_dns(int ifindex, char ***ret); -/* Get DHCP4 address for a given link. This is string representations of - * IPv4 address */ -int sd_network_link_get_dhcp4_address(int ifindex, char **ret); - /* Get NTP entries for a given link. These are domain names or string * representations of IP addresses */ int sd_network_link_get_ntp(int ifindex, char ***ret); +/* Get SIP entries for a given link. These are string + * representations of IP addresses */ +int sd_network_link_get_sip(int ifindex, char ***ret); + /* Indicates whether or not LLMNR should be enabled for the link * Possible levels of support: yes, no, resolve * Possible return codes: @@ -160,9 +160,6 @@ int sd_network_link_get_search_domains(int ifindex, char ***domains); /* Get the route DNS domain names for a given link. */ int sd_network_link_get_route_domains(int ifindex, char ***domains); -/* Get the sip servers for a given link. */ -int sd_network_link_get_sip_servers(int ifindex, char ***sip); - /* Get whether this link shall be used as 'default route' for DNS queries */ int sd_network_link_get_dns_default_route(int ifindex); @@ -172,8 +169,11 @@ int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); /* Get the CARRIERS that are bound to current link. */ int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); -/* Get the timezone that was learnt on a specific link. */ -int sd_network_link_get_timezone(int ifindex, char **timezone); +/* Get DHCPv6 client IAID for a given link. */ +int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid); + +/* Get DHCPv6 client DUID for a given link. */ +int sd_network_link_get_dhcp6_client_duid_string(int ifindex, char **duid); /* Monitor object */ typedef struct sd_network_monitor sd_network_monitor; diff --git a/src/systemd/sd-path.h b/src/systemd/sd-path.h index 16379876e..e13e67db8 100644 --- a/src/systemd/sd-path.h +++ b/src/systemd/sd-path.h @@ -78,11 +78,43 @@ enum { SD_PATH_SEARCH_STATE_FACTORY, SD_PATH_SEARCH_CONFIGURATION, + /* Various systemd paths, generally mirroring systemd.pc — Except we drop the "dir" suffix (and + * replaces "path" by "search"), since this API is about dirs/paths anyway, and contains "path" + * already in the prefix */ + SD_PATH_SYSTEMD_UTIL, + SD_PATH_SYSTEMD_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SYSTEM_PRESET, + SD_PATH_SYSTEMD_SYSTEM_CONF, + SD_PATH_SYSTEMD_USER_UNIT, + SD_PATH_SYSTEMD_USER_PRESET, + SD_PATH_SYSTEMD_USER_CONF, + + SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT, + SD_PATH_SYSTEMD_SEARCH_USER_UNIT, + + SD_PATH_SYSTEMD_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_USER_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR, + SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR, + + SD_PATH_SYSTEMD_SLEEP, + SD_PATH_SYSTEMD_SHUTDOWN, + + SD_PATH_TMPFILES, + SD_PATH_SYSUSERS, + SD_PATH_SYSCTL, + SD_PATH_BINFMT, + SD_PATH_MODULES_LOAD, + SD_PATH_CATALOG, + + /* systemd-networkd search paths */ + SD_PATH_SYSTEMD_SEARCH_NETWORK, + _SD_PATH_MAX, }; -int sd_path_home(uint64_t type, const char *suffix, char **path); -int sd_path_search(uint64_t type, const char *suffix, char ***paths); +int sd_path_lookup(uint64_t type, const char *suffix, char **path); +int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths); _SD_END_DECLARATIONS; diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index f08523193..0f1437829 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -50,6 +50,7 @@ sd_event *sd_radv_get_event(sd_radv *ra); int sd_radv_start(sd_radv *ra); int sd_radv_stop(sd_radv *ra); +int sd_radv_is_running(sd_radv *ra); int sd_radv_set_ifindex(sd_radv *ra, int interface_index); int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr); @@ -74,6 +75,8 @@ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra); int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen); +int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr, + unsigned char *ret_prefixlen); int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink); int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index fc0855d61..b5e7e08ee 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -345,28 +345,6 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { } #endif -static int sync_rights(FILE *from, const char *to) { - struct stat st; - - if (fstat(fileno(from), &st) < 0) - return -errno; - - return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid); -} - -static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { - int r = 0; - if (rename(temp_path, dest_path) < 0) - return -errno; - -#ifdef SMACK_RUN_LABEL - r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} - static const char* default_shell(uid_t uid) { return uid == 0 ? "/bin/sh" : NOLOGIN; } @@ -389,7 +367,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char original = fopen(passwd_path, "re"); if (original) { - r = sync_rights(original, passwd_tmp); + r = sync_rights(fileno(original), fileno(passwd)); if (r < 0) return r; @@ -491,7 +469,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char original = fopen(shadow_path, "re"); if (original) { - r = sync_rights(original, shadow_tmp); + r = sync_rights(fileno(original), fileno(shadow)); if (r < 0) return r; @@ -588,7 +566,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** original = fopen(group_path, "re"); if (original) { - r = sync_rights(original, group_tmp); + r = sync_rights(fileno(original), fileno(group)); if (r < 0) return r; @@ -687,7 +665,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch if (original) { struct sgrp *sg; - r = sync_rights(original, gshadow_tmp); + r = sync_rights(fileno(original), fileno(gshadow)); if (r < 0) return r; @@ -794,14 +772,14 @@ static int write_files(void) { /* And make the new files count */ if (group) { - r = rename_and_apply_smack(group_tmp, group_path); + r = rename_and_apply_smack_floor_label(group_tmp, group_path); if (r < 0) return r; group_tmp = mfree(group_tmp); } if (gshadow) { - r = rename_and_apply_smack(gshadow_tmp, gshadow_path); + r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path); if (r < 0) return r; @@ -809,14 +787,14 @@ static int write_files(void) { } if (passwd) { - r = rename_and_apply_smack(passwd_tmp, passwd_path); + r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path); if (r < 0) return r; passwd_tmp = mfree(passwd_tmp); } if (shadow) { - r = rename_and_apply_smack(shadow_tmp, shadow_path); + r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path); if (r < 0) return r; @@ -1389,12 +1367,18 @@ static bool item_equal(Item *a, Item *b) { static int parse_line(const char *fname, unsigned line, const char *buffer) { static const Specifier specifier_table[] = { - { 'm', specifier_machine_id, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - { 'T', specifier_tmp_dir, NULL }, - { 'V', specifier_var_tmp_dir, NULL }, + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + { 'T', specifier_tmp_dir, NULL }, + { 'V', specifier_var_tmp_dir, NULL }, {} }; @@ -1808,7 +1792,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); + r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_root); if (r < 0) return r; break; @@ -1914,7 +1898,7 @@ static int run(int argc, char *argv[]) { r = mac_selinux_init(); if (r < 0) - return log_error_errno(r, "SELinux setup failed: %m"); + return r; /* If command line arguments are specified along with --replace, read all * configuration files and insert the positional arguments at the specified diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 5df574382..a2c72d100 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -788,19 +788,25 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (!fpath) return log_oom(); - service = new0(SysvStub, 1); + log_warning("SysV service '%s' lacks a native systemd unit file. " + "Automatically generating a unit file for compatibility. " + "Please update package to include a native systemd unit file, in order to make it more safe and robust.", fpath); + + service = new(SysvStub, 1); if (!service) return log_oom(); - service->sysv_start_priority = -1; - service->name = TAKE_PTR(name); - service->path = TAKE_PTR(fpath); + *service = (SysvStub) { + .sysv_start_priority = -1, + .name = TAKE_PTR(name), + .path = TAKE_PTR(fpath), + }; r = hashmap_put(all_services, service->name, service); if (r < 0) return log_oom(); - service = NULL; + TAKE_PTR(service); } } @@ -811,7 +817,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; - unsigned i; Iterator j; char **p; int r; @@ -822,9 +827,8 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic if (r < 0) return r; - STRV_FOREACH(p, sysvrcnd_path) { - for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { - + STRV_FOREACH(p, sysvrcnd_path) + for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i ++) { _cleanup_closedir_ DIR *d = NULL; _cleanup_free_ char *path = NULL; struct dirent *de; @@ -843,7 +847,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic continue; } - FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) { + FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) { _cleanup_free_ char *name = NULL, *fpath = NULL; int a, b; @@ -879,22 +883,15 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority); - r = set_ensure_allocated(&runlevel_services[i], NULL); - if (r < 0) { - log_oom(); - goto finish; - } - - r = set_put(runlevel_services[i], service); + r = set_ensure_put(&runlevel_services[i], NULL, service); if (r < 0) { log_oom(); goto finish; } } } - } - for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) + for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++) SET_FOREACH(service, runlevel_services[i], j) { r = strv_extend(&service->before, rcnd_table[i].target); if (r < 0) { @@ -911,7 +908,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic r = 0; finish: - for (i = 0; i < ELEMENTSOF(rcnd_table); i++) + for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++) set_free(runlevel_services[i]); return r; diff --git a/src/test/meson.build b/src/test/meson.build index a150ee106..132989f19 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -301,6 +301,12 @@ tests += [ [], []], + [['src/test/test-offline-passwd.c', + 'src/shared/offline-passwd.c', + 'src/shared/offline-passwd.h'], + [], + []], + [['src/test/test-escape.c'], [], []], @@ -470,7 +476,7 @@ tests += [ '', 'timeout=90'], [['src/test/test-set.c'], - [], + [libbasic], []], [['src/test/test-ordered-set.c'], @@ -588,14 +594,17 @@ tests += [ [], []], + [['src/test/test-coredump-util.c'], + [], + []], + [['src/test/test-daemon.c'], [], []], [['src/test/test-cgroup.c'], [], - [], - '', 'manual'], + []], [['src/test/test-cgroup-cpu.c'], [libcore, @@ -662,7 +671,8 @@ tests += [ libseccomp, libselinux, libmount, - libblkid]], + libblkid], + '', 'timeout=120'], [['src/test/test-execute.c'], [libcore, @@ -779,6 +789,10 @@ tests += [ [], []], + [['src/test/test-sd-path.c'], + [], + []], + [['src/test/test-local-addresses.c'], [], []], @@ -886,12 +900,14 @@ tests += [ [libjournal_core, libshared], [liblz4, + libzstd, libxz]], [['src/journal/test-compress-benchmark.c'], [libjournal_core, libshared], [liblz4, + libzstd, libxz], '', 'timeout=90'], @@ -1023,10 +1039,7 @@ tests += [ ] -# test-bus-vtable-cc.cc is a symlink and symlinks get lost in containers on FuzzBuzz. -# The issue has been reported to the developers of FuzzBuzz and hopefully will be fixed soon. -# In the meantime, let's just skip the symlink there. -if cxx_cmd != '' and not want_fuzzbuzz +if cxx_cmd != '' tests += [ [['src/libsystemd/sd-bus/test-bus-vtable-cc.cc'], [], @@ -1137,3 +1150,13 @@ tests += [ libshared], [threads]], ] + +############################################################ + +tests += [ + [['src/test/test-xdg-autostart.c', + 'src/xdg-autostart-generator/xdg-autostart-service.c', + 'src/xdg-autostart-generator/xdg-autostart-service.h',], + [], + []], +] diff --git a/src/test/test-bpf-devices.c b/src/test/test-bpf-devices.c index 1322af481..d2740bca7 100644 --- a/src/test/test-bpf-devices.c +++ b/src/test/test-bpf-devices.c @@ -24,7 +24,7 @@ static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_p r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true); assert_se(r >= 0); - r = bpf_devices_whitelist_static(prog, cgroup_path); + r = bpf_devices_allow_list_static(prog, cgroup_path); assert_se(r >= 0); r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog); @@ -62,13 +62,13 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); assert_se(r >= 0); - r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/null", "rw"); + r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", "rw"); assert_se(r >= 0); - r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/random", "r"); + r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", "r"); assert_se(r >= 0); - r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/zero", "w"); + r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", "w"); assert_se(r >= 0); r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); @@ -129,7 +129,7 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p assert_se(wrong == 0); } -static void test_policy_whitelist_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) { +static void test_policy_allow_list_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) { _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; unsigned wrong = 0; int r; @@ -139,7 +139,7 @@ static void test_policy_whitelist_major(const char *pattern, const char *cgroup_ r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); assert_se(r >= 0); - r = bpf_devices_whitelist_major(prog, cgroup_path, pattern, 'c', "rw"); + r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', "rw"); assert_se(r >= 0); r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); @@ -188,7 +188,7 @@ static void test_policy_whitelist_major(const char *pattern, const char *cgroup_ assert_se(wrong == 0); } -static void test_policy_whitelist_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) { +static void test_policy_allow_list_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) { _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; unsigned wrong = 0; int r; @@ -198,7 +198,7 @@ static void test_policy_whitelist_major_star(char type, const char *cgroup_path, r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true); assert_se(r >= 0); - r = bpf_devices_whitelist_major(prog, cgroup_path, "*", type, "rw"); + r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, "rw"); assert_se(r >= 0); r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog); @@ -230,7 +230,7 @@ static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFP assert_se(r >= 0); if (add_mismatched) { - r = bpf_devices_whitelist_major(prog, cgroup_path, "foobarxxx", 'c', "rw"); + r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', "rw"); assert_se(r < 0); } @@ -287,11 +287,11 @@ int main(int argc, char *argv[]) { test_policy_closed(cgroup, &prog); test_policy_strict(cgroup, &prog); - test_policy_whitelist_major("mem", cgroup, &prog); - test_policy_whitelist_major("1", cgroup, &prog); + test_policy_allow_list_major("mem", cgroup, &prog); + test_policy_allow_list_major("1", cgroup, &prog); - test_policy_whitelist_major_star('c', cgroup, &prog); - test_policy_whitelist_major_star('b', cgroup, &prog); + test_policy_allow_list_major_star('c', cgroup, &prog); + test_policy_allow_list_major_star('b', cgroup, &prog); test_policy_empty(false, cgroup, &prog); test_policy_empty(true, cgroup, &prog); diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index fbaa349b4..71aed1255 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -48,7 +48,9 @@ int main(int argc, char *argv[]) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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 = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p); diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c index 33dd2461c..81d5c456d 100644 --- a/src/test/test-cap-list.c +++ b/src/test/test-cap-list.c @@ -12,12 +12,10 @@ /* verify the capability parser */ static void test_cap_list(void) { - int i; - assert_se(!capability_to_name(-1)); assert_se(!capability_to_name(capability_list_length())); - for (i = 0; i < capability_list_length(); i++) { + for (int i = 0; i < capability_list_length(); i++) { const char *n; assert_se(n = capability_to_name(i)); @@ -35,7 +33,7 @@ static void test_cap_list(void) { assert_se(capability_from_name("64") == -EINVAL); assert_se(capability_from_name("-1") == -EINVAL); - for (i = 0; i < capability_list_length(); i++) { + for (int i = 0; i < capability_list_length(); i++) { _cleanup_cap_free_charp_ char *a = NULL; const char *b; unsigned u; diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 74b27379e..249323f8c 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "capability-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "macro.h" @@ -35,6 +36,8 @@ static void test_last_cap_file(void) { int r; r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */ + return; assert_se(r >= 0); r = safe_atolu(content, &val); @@ -230,7 +233,7 @@ static void test_ensure_cap_64bit(void) { int r; r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); - if (r == -ENOENT) /* kernel pre 3.2 */ + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */ return; assert_se(r >= 0); @@ -240,7 +243,7 @@ static void test_ensure_cap_64bit(void) { assert_se(p <= 63); /* Also check for the header definition */ - assert_se(CAP_LAST_CAP <= 63); + assert_cc(CAP_LAST_CAP <= 63); } int main(int argc, char *argv[]) { diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 02c8549b4..daafba4ef 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -38,7 +38,9 @@ static int test_cgroup_mask(void) { return log_tests_skipped("cgroupfs not available"); /* Prepare the manager. */ - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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); if (IN_SET(r, -EPERM, -EACCES)) { diff --git a/src/test/test-cgroup-setup.c b/src/test/test-cgroup-setup.c index 330631a91..25fa0d75d 100644 --- a/src/test/test-cgroup-setup.c +++ b/src/test/test-cgroup-setup.c @@ -1,8 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "build.h" #include "cgroup-setup.h" +#include "errno-util.h" #include "log.h" #include "proc-cmdline.h" #include "string-util.h" @@ -59,6 +62,9 @@ static void test_is_wanted(void) { int main(void) { test_setup_logging(LOG_DEBUG); + if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return log_tests_skipped("can't read /proc/cmdline"); + test_is_wanted_print(true); test_is_wanted_print(false); /* run twice to test caching */ test_is_wanted(); diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c index 1286f11e4..f4843374e 100644 --- a/src/test/test-cgroup-unit-default.c +++ b/src/test/test-cgroup-unit-default.c @@ -22,7 +22,9 @@ static int test_default_memory_low(void) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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); if (IN_SET(r, -EPERM, -EACCES)) { @@ -70,7 +72,7 @@ static int test_default_memory_low(void) { * * 3. dml-discard.slice sets DefaultMemoryLow= with no rvalue. As such, * dml-discard-empty.service should end up with a value of 0. - * dml-discard-explicit-ml.service sets MemoryLow=70, and as such should have that override the + * dml-discard-set-ml.service sets MemoryLow=15, and as such should have that override the * reset DefaultMemoryLow value. dml-discard.slice should still have an eventual memory.low of 50. * * ┌───────────┐ diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 83b5156c1..eff586a2e 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -4,6 +4,7 @@ #include "build.h" #include "cgroup-util.h" #include "dirent-util.h" +#include "errno-util.h" #include "fd-util.h" #include "format-util.h" #include "parse-util.h" @@ -22,7 +23,7 @@ static void check_p_d_u(const char *path, int code, const char *result) { int r; r = cg_path_decode_unit(path, &unit); - printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code); + printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code); assert_se(r == code); assert_se(streq_ptr(unit, result)); } @@ -44,7 +45,7 @@ static void check_p_g_u(const char *path, int code, const char *result) { int r; r = cg_path_get_unit(path, &unit); - printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code); + printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code); assert_se(r == code); assert_se(streq_ptr(unit, result)); } @@ -68,7 +69,7 @@ static void check_p_g_u_u(const char *path, int code, const char *result) { int r; r = cg_path_get_user_unit(path, &unit); - printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code); + printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code); assert_se(r == code); assert_se(streq_ptr(unit, result)); } @@ -369,7 +370,7 @@ static void test_cg_get_keyed_attribute(void) { int i, r; r = cg_get_keyed_attribute("cpu", "/init.scope", "no_such_file", STRV_MAKE("no_such_attr"), &val); - if (r == -ENOMEDIUM) { + if (r == -ENOMEDIUM || ERRNO_IS_PRIVILEGE(r)) { log_info_errno(r, "Skipping most of %s, /sys/fs/cgroup not accessible: %m", __func__); return; } @@ -383,22 +384,42 @@ static void test_cg_get_keyed_attribute(void) { } assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO); + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0); assert_se(val == NULL); assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0); + val = mfree(val); + + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 1); log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val); assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO); + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1); + assert(vals3[0] && !vals3[1]); + free(vals3[0]); assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO); + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1); + assert(vals3[0] && !vals3[1]); + free(vals3[0]); assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0); + for (i = 0; i < 3; i++) + free(vals3[i]); + + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", + STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 3); log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"", vals3[0], vals3[1], vals3[2]); assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0); + for (i = 0; i < 3; i++) + free(vals3a[i]); + + assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", + STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 3); log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"", vals3a[0], vals3a[1], vals3a[2]); diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index f16cb291f..4fbb186f5 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -4,69 +4,27 @@ #include "cgroup-setup.h" #include "cgroup-util.h" +#include "errno-util.h" #include "path-util.h" #include "process-util.h" #include "string-util.h" -#include "util.h" +#include "tests.h" -int main(int argc, char *argv[]) { - char *path; +static void test_cg_split_spec(void) { char *c, *p; - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0); - assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0); - - assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); - assert_se(streq(path, "/test-b")); - free(path); - - assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) == 0); - - assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); - assert_se(path_equal(path, "/test-a")); - free(path); - - assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0); - - assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); - assert_se(path_equal(path, "/test-b/test-d")); - free(path); - - assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", NULL, &path) == 0); - assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d")); - free(path); - - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); - - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) == 0); - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) > 0); - - assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) > 0); - - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); - - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) > 0); - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) == 0); - - cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false); - - assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0); - assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0); + log_info("/* %s */", __func__); assert_se(cg_split_spec("foobar:/", &c, &p) == 0); assert_se(streq(c, "foobar")); assert_se(streq(p, "/")); - free(c); - free(p); + c = mfree(c); + p = mfree(p); + + assert_se(cg_split_spec("foobar:", &c, &p) == 0); + c = mfree(c); + p = mfree(p); - assert_se(cg_split_spec("foobar:", &c, &p) < 0); assert_se(cg_split_spec("foobar:asdfd", &c, &p) < 0); assert_se(cg_split_spec(":///", &c, &p) < 0); assert_se(cg_split_spec(":", &c, &p) < 0); @@ -76,12 +34,104 @@ int main(int argc, char *argv[]) { assert_se(cg_split_spec("/", &c, &p) >= 0); assert_se(c == NULL); assert_se(streq(p, "/")); - free(p); + p = mfree(p); assert_se(cg_split_spec("foo", &c, &p) >= 0); assert_se(streq(c, "foo")); assert_se(p == NULL); - free(c); + c = mfree(c); +} + +static void test_cg_create(void) { + log_info("/* %s */", __func__); + int r; + + r = cg_unified_cached(false); + if (r < 0) { + log_info_errno(r, "Skipping %s: %m", __func__); + return; + } + + _cleanup_free_ char *here = NULL; + assert_se(cg_pid_get_path_shifted(0, NULL, &here) >= 0); + + const char *test_a = prefix_roota(here, "/test-a"), + *test_b = prefix_roota(here, "/test-b"), + *test_c = prefix_roota(here, "/test-b/test-c"), + *test_d = prefix_roota(here, "/test-b/test-d"); + char *path; + + log_info("Paths for test:\n%s\n%s", test_a, test_b); + + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, test_a); + if (IN_SET(r, -EPERM, -EACCES, -EROFS)) { + log_info_errno(r, "Skipping %s: %m", __func__); + return; + } + + assert_se(r == 1); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_b) == 1); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_c) == 1); + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_b, 0) == 0); + + assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); + assert_se(streq(path, test_b)); + free(path); + + assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, test_a, 0) == 0); + + assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); + assert_se(path_equal(path, test_a)); + free(path); + + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_d, 0) == 1); + + assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0); + assert_se(path_equal(path, test_d)); + free(path); + + assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, test_d, NULL, &path) == 0); + log_debug("test_d: %s", path); + const char *full_d; + if (cg_all_unified()) + full_d = strjoina("/sys/fs/cgroup", test_d); + else if (cg_hybrid_unified()) + full_d = strjoina("/sys/fs/cgroup/unified", test_d); + else + full_d = strjoina("/sys/fs/cgroup/systemd", test_d); + assert_se(path_equal(path, full_d)); + free(path); + + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_a) > 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_b) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b) == 0); + + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, 0, 0, NULL, NULL, NULL) == 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, 0, 0, NULL, NULL, NULL) > 0); + + assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, SYSTEMD_CGROUP_CONTROLLER, test_a, 0) > 0); + + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b) > 0); + + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, 0, 0, NULL, NULL, NULL) > 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, 0, 0, NULL, NULL, NULL) == 0); + + cg_trim(SYSTEMD_CGROUP_CONTROLLER, test_b, false); + + assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_b) == 0); + assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) < 0); + assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, SYSTEMD_CGROUP_CONTROLLER, here, 0) > 0); + assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_cg_split_spec(); + test_cg_create(); return 0; } diff --git a/src/test/test-chase-symlinks.c b/src/test/test-chase-symlinks.c index 0b7dd8764..f7b3dd5e0 100644 --- a/src/test/test-chase-symlinks.c +++ b/src/test/test-chase-symlinks.c @@ -84,9 +84,7 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char **argv) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/test/test-clock.c b/src/test/test-clock.c index dc310939a..271d46554 100644 --- a/src/test/test-clock.c +++ b/src/test/test-clock.c @@ -60,14 +60,14 @@ static void test_clock_is_localtime_system(void) { int r; r = clock_is_localtime(NULL); - if (access("/etc/adjtime", F_OK) == 0) { - log_info("/etc/adjtime exists, clock_is_localtime() == %i", r); + if (access("/etc/adjtime", R_OK) == 0) { + log_info("/etc/adjtime is readable, clock_is_localtime() == %i", r); /* if /etc/adjtime exists we expect some answer, no error or * crash */ assert_se(IN_SET(r, 0, 1)); } else /* default is UTC if there is no /etc/adjtime */ - assert_se(r == 0); + assert_se(r == 0 || ERRNO_IS_PRIVILEGE(r)); } int main(int argc, char *argv[]) { diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 28b5b780c..ddf2e669c 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -15,6 +15,7 @@ #include "condition.h" #include "cpu-set-util.h" #include "efi-loader.h" +#include "errno-util.h" #include "hostname-util.h" #include "id128-util.h" #include "ima-util.h" @@ -38,82 +39,87 @@ static void test_condition_test_path(void) { condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false); assert_se(condition); - assert_se(condition_test(condition)); + assert_se(condition_test(condition, environ)); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_IS_ENCRYPTED, "/sys", false, false); + assert_se(condition); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); } @@ -133,12 +139,12 @@ static void test_condition_test_control_group_controller(void) { /* Invalid controllers are ignored */ condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); assert_se(cg_mask_supported(&system_mask) >= 0); @@ -151,23 +157,23 @@ static void test_condition_test_control_group_controller(void) { log_info("this controller is available"); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } else { log_info("this controller is unavailable"); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); } } @@ -177,12 +183,12 @@ static void test_condition_test_control_group_controller(void) { condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } @@ -191,17 +197,17 @@ static void test_condition_test_ac_power(void) { condition = condition_new(CONDITION_AC_POWER, "true", false, false); assert_se(condition); - assert_se(condition_test(condition) == on_ac_power()); + assert_se(condition_test(condition, environ) == on_ac_power()); condition_free(condition); condition = condition_new(CONDITION_AC_POWER, "false", false, false); assert_se(condition); - assert_se(condition_test(condition) != on_ac_power()); + assert_se(condition_test(condition, environ) != on_ac_power()); condition_free(condition); condition = condition_new(CONDITION_AC_POWER, "false", false, true); assert_se(condition); - assert_se(condition_test(condition) == on_ac_power()); + assert_se(condition_test(condition, environ) == on_ac_power()); condition_free(condition); } @@ -218,17 +224,17 @@ static void test_condition_test_host(void) { condition = condition_new(CONDITION_HOST, sid, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_HOST, sid, false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); hostname = gethostname_malloc(); @@ -240,7 +246,7 @@ static void test_condition_test_host(void) { else { condition = condition_new(CONDITION_HOST, hostname, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); } } @@ -258,31 +264,35 @@ static void test_condition_test_architecture(void) { condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } static void test_condition_test_kernel_command_line(void) { Condition *condition; + int r; condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + r = condition_test(condition, environ); + if (ERRNO_IS_PRIVILEGE(r)) + return; + assert_se(r == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } @@ -293,26 +303,26 @@ static void test_condition_test_kernel_version(void) { condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); /* An artificially empty condition. It evaluates to true, but normally * such condition cannot be created, because the condition list is reset instead. */ condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); assert_se(uname(&u) >= 0); condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); strshorten(u.release, 4); @@ -320,79 +330,79 @@ static void test_condition_test_kernel_version(void) { condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); /* 0.1.2 would be a very very very old kernel */ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, ">0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "'>0.1.2' '<9.0.0'", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2 < 9.0.0", false, false); assert_se(condition); - assert_se(condition_test(condition) == -EINVAL); + assert_se(condition_test(condition, environ) == -EINVAL); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, ">", false, false); assert_se(condition); - assert_se(condition_test(condition) == -EINVAL); + assert_se(condition_test(condition, environ) == -EINVAL); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); /* 4711.8.15 is a very very very future kernel */ condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); assert_se(uname(&u) >= 0); @@ -400,31 +410,31 @@ static void test_condition_test_kernel_version(void) { v = strjoina(">=", u.release); condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); v = strjoina("= ", u.release); condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); v = strjoina("<=", u.release); condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); v = strjoina("> ", u.release); condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); v = strjoina("< ", u.release); condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } @@ -433,12 +443,12 @@ static void test_condition_test_null(void) { condition = condition_new(CONDITION_NULL, NULL, false, false); assert_se(condition); - assert_se(condition_test(condition) > 0); + assert_se(condition_test(condition, environ) > 0); condition_free(condition); condition = condition_new(CONDITION_NULL, NULL, false, true); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); } @@ -447,42 +457,42 @@ static void test_condition_test_security(void) { condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false); assert_se(condition); - assert_se(condition_test(condition) == 0); + assert_se(condition_test(condition, environ) == 0); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "selinux", false, true); assert_se(condition); - assert_se(condition_test(condition) != mac_selinux_use()); + assert_se(condition_test(condition, environ) != mac_selinux_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "apparmor", false, false); assert_se(condition); - assert_se(condition_test(condition) == mac_apparmor_use()); + assert_se(condition_test(condition, environ) == mac_apparmor_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "tomoyo", false, false); assert_se(condition); - assert_se(condition_test(condition) == mac_tomoyo_use()); + assert_se(condition_test(condition, environ) == mac_tomoyo_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "ima", false, false); assert_se(condition); - assert_se(condition_test(condition) == use_ima()); + assert_se(condition_test(condition, environ) == use_ima()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "smack", false, false); assert_se(condition); - assert_se(condition_test(condition) == mac_smack_use()); + assert_se(condition_test(condition, environ) == mac_smack_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "audit", false, false); assert_se(condition); - assert_se(condition_test(condition) == use_audit()); + assert_se(condition_test(condition, environ) == use_audit()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "uefi-secureboot", false, false); assert_se(condition); - assert_se(condition_test(condition) == is_efi_secure_boot()); + assert_se(condition_test(condition, environ) == is_efi_secure_boot()); condition_free(condition); } @@ -505,28 +515,30 @@ static void test_condition_test_virtualization(void) { condition = condition_new(CONDITION_VIRTUALIZATION, "garbage oifdsjfoidsjoj", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); + if (ERRNO_IS_PRIVILEGE(r)) + return; log_info("ConditionVirtualization=garbage → %i", r); assert_se(r == 0); condition_free(condition); condition = condition_new(CONDITION_VIRTUALIZATION, "container", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionVirtualization=container → %i", r); assert_se(r == !!detect_container()); condition_free(condition); condition = condition_new(CONDITION_VIRTUALIZATION, "vm", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionVirtualization=vm → %i", r); assert_se(r == (detect_vm() && !detect_container())); condition_free(condition); condition = condition_new(CONDITION_VIRTUALIZATION, "private-users", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionVirtualization=private-users → %i", r); assert_se(r == !!running_in_userns()); condition_free(condition); @@ -547,7 +559,7 @@ static void test_condition_test_virtualization(void) { condition = condition_new(CONDITION_VIRTUALIZATION, virt, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionVirtualization=%s → %i", virt, r); assert_se(r >= 0); condition_free(condition); @@ -562,7 +574,7 @@ static void test_condition_test_user(void) { condition = condition_new(CONDITION_USER, "garbage oifdsjfoidsjoj", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=garbage → %i", r); assert_se(r == 0); condition_free(condition); @@ -570,7 +582,7 @@ static void test_condition_test_user(void) { assert_se(asprintf(&uid, "%"PRIu32, UINT32_C(0xFFFF)) > 0); condition = condition_new(CONDITION_USER, uid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=%s → %i", uid, r); assert_se(r == 0); condition_free(condition); @@ -579,7 +591,7 @@ static void test_condition_test_user(void) { assert_se(asprintf(&uid, "%u", (unsigned)getuid()) > 0); condition = condition_new(CONDITION_USER, uid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=%s → %i", uid, r); assert_se(r > 0); condition_free(condition); @@ -588,7 +600,7 @@ static void test_condition_test_user(void) { assert_se(asprintf(&uid, "%u", (unsigned)getuid()+1) > 0); condition = condition_new(CONDITION_USER, uid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=%s → %i", uid, r); assert_se(r == 0); condition_free(condition); @@ -598,7 +610,7 @@ static void test_condition_test_user(void) { assert_se(username); condition = condition_new(CONDITION_USER, username, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=%s → %i", username, r); assert_se(r > 0); condition_free(condition); @@ -607,14 +619,14 @@ static void test_condition_test_user(void) { username = (char*)(geteuid() == 0 ? NOBODY_USER_NAME : "root"); condition = condition_new(CONDITION_USER, username, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=%s → %i", username, r); assert_se(r == 0); condition_free(condition); condition = condition_new(CONDITION_USER, "@system", false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionUser=@system → %i", r); if (uid_is_system(getuid()) || uid_is_system(geteuid())) assert_se(r > 0); @@ -633,7 +645,7 @@ static void test_condition_test_group(void) { assert_se(0 < asprintf(&gid, "%u", UINT32_C(0xFFFF))); condition = condition_new(CONDITION_GROUP, gid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", gid, r); assert_se(r == 0); condition_free(condition); @@ -642,7 +654,7 @@ static void test_condition_test_group(void) { assert_se(0 < asprintf(&gid, "%u", getgid())); condition = condition_new(CONDITION_GROUP, gid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", gid, r); assert_se(r > 0); condition_free(condition); @@ -661,7 +673,7 @@ static void test_condition_test_group(void) { assert_se(0 < asprintf(&gid, "%u", gids[i])); condition = condition_new(CONDITION_GROUP, gid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", gid, r); assert_se(r > 0); condition_free(condition); @@ -672,7 +684,7 @@ static void test_condition_test_group(void) { assert_se(groupname); condition = condition_new(CONDITION_GROUP, groupname, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", groupname, r); assert_se(r > 0); condition_free(condition); @@ -683,7 +695,7 @@ static void test_condition_test_group(void) { assert_se(0 < asprintf(&gid, "%u", max_gid+1)); condition = condition_new(CONDITION_GROUP, gid, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", gid, r); assert_se(r == 0); condition_free(condition); @@ -692,7 +704,7 @@ static void test_condition_test_group(void) { groupname = (char*)(getegid() == 0 ? NOBODY_GROUP_NAME : "root"); condition = condition_new(CONDITION_GROUP, groupname, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); log_info("ConditionGroup=%s → %i", groupname, r); assert_se(r == 0); condition_free(condition); @@ -707,7 +719,7 @@ static void test_condition_test_cpus_one(const char *s, bool result) { condition = condition_new(CONDITION_CPUS, s, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); assert_se(r >= 0); assert_se(r == result); condition_free(condition); @@ -768,7 +780,7 @@ static void test_condition_test_memory_one(const char *s, bool result) { condition = condition_new(CONDITION_MEMORY, s, false, false); assert_se(condition); - r = condition_test(condition); + r = condition_test(condition, environ); assert_se(r >= 0); assert_se(r == result); condition_free(condition); @@ -819,6 +831,34 @@ static void test_condition_test_memory(void) { t = mfree(t); } +static void test_condition_test_environment_one(const char *s, bool result) { + Condition *condition; + int r; + + log_debug("%s=%s", condition_type_to_string(CONDITION_ENVIRONMENT), s); + + condition = condition_new(CONDITION_ENVIRONMENT, s, false, false); + assert_se(condition); + + r = condition_test(condition, environ); + assert_se(r >= 0); + assert_se(r == result); + condition_free(condition); +} + +static void test_condition_test_environment(void) { + assert_se(setenv("EXISTINGENVVAR", "foo", false) >= 0); + + test_condition_test_environment_one("MISSINGENVVAR", false); + test_condition_test_environment_one("MISSINGENVVAR=foo", false); + test_condition_test_environment_one("MISSINGENVVAR=", false); + + test_condition_test_environment_one("EXISTINGENVVAR", true); + test_condition_test_environment_one("EXISTINGENVVAR=foo", true); + test_condition_test_environment_one("EXISTINGENVVAR=bar", false); + test_condition_test_environment_one("EXISTINGENVVAR=", false); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -837,6 +877,7 @@ int main(int argc, char *argv[]) { test_condition_test_control_group_controller(); test_condition_test_cpus(); test_condition_test_memory(); + test_condition_test_environment(); return 0; } diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index 510b1de72..07edc17f9 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -335,13 +335,17 @@ static void test_config_parse(unsigned i, const char *s) { ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, - void *userdata) + void *userdata, + usec_t *ret_mtime) */ r = config_parse(NULL, name, f, - "Section\0-NoWarnSection\0", + "Section\0" + "-NoWarnSection\0", config_item_table_lookup, items, - CONFIG_PARSE_WARN, NULL); + CONFIG_PARSE_WARN, + NULL, + NULL); switch (i) { case 0 ... 4: diff --git a/src/test/test-copy.c b/src/test/test-copy.c index 68905c662..0e8d68795 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -78,8 +78,8 @@ static void test_copy_file_fd(void) { } static void test_copy_tree(void) { - char original_dir[] = "/var/tmp/test-copy_tree/"; - char copy_dir[] = "/var/tmp/test-copy_tree-copy/"; + char original_dir[] = "/tmp/test-copy_tree/"; + char copy_dir[] = "/tmp/test-copy_tree-copy/"; char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file"); char **links = STRV_MAKE("link", "file", "link2", "dir1/file"); @@ -270,7 +270,7 @@ static void test_copy_atomic(void) { q = strjoina(p, "/fstab"); r = copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK); - if (r == -ENOENT) + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) return; assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK) == -EEXIST); diff --git a/src/test/test-coredump-util.c b/src/test/test-coredump-util.c new file mode 100644 index 000000000..14a78007e --- /dev/null +++ b/src/test/test-coredump-util.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "coredump-util.h" +#include "macro.h" +#include "tests.h" + +static void test_coredump_filter_to_from_string(void) { + log_info("/* %s */", __func__); + + for (CoredumpFilter i = 0; i < _COREDUMP_FILTER_MAX; i++) { + const char *n; + + assert_se(n = coredump_filter_to_string(i)); + log_info("0x%x\t%s", 1<= 0); - f = prefix_roota(p, "/run/systemd"); assert_se(make_inaccessible_nodes(f, 1, 1) >= 0); f = prefix_roota(p, "/run/systemd/inaccessible/reg"); diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c index a1ccf605b..13ff8add5 100644 --- a/src/test/test-dissect-image.c +++ b/src/test/test-dissect-image.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); + r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m); if (r < 0) { log_error_errno(r, "Failed to dissect image: %m"); return EXIT_FAILURE; diff --git a/src/test/test-engine.c b/src/test/test-engine.c index b8351141f..6465151b2 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -26,8 +26,11 @@ int main(int argc, char *argv[]) { return log_tests_skipped("cgroupfs not available"); /* prepare the test */ - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); diff --git a/src/test/test-escape.c b/src/test/test-escape.c index f6aae1eb1..699747fcc 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -142,31 +142,42 @@ static void test_shell_maybe_quote_one(const char *s, static void test_shell_maybe_quote(void) { test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, ""); + test_shell_maybe_quote_one("", ESCAPE_BACKSLASH_ONELINE, ""); test_shell_maybe_quote_one("", ESCAPE_POSIX, ""); test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\""); + test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH_ONELINE, "\"\\\\\""); test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'"); test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\""); + test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH_ONELINE, "\"\\\"\""); test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'"); test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar"); + test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH_ONELINE, "foobar"); test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar"); test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\""); + test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH_ONELINE, "\"foo bar\""); test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'"); test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\""); + test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\tbar\""); test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'"); test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\""); + test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\nbar\""); test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'"); test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\""); + test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH_ONELINE, "\"foo \\\"bar\\\" waldo\""); test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'"); test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\""); + test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\$bar\""); test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'"); /* Note that current users disallow control characters, so this "test" * is here merely to establish current behaviour. If control characters * were allowed, they should be quoted, i.e. \001 should become \\001. */ test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\""); + test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH_ONELINE, "\"a\\nb\001\""); test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'"); test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\""); + test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH_ONELINE, "\"foo!bar\""); test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'"); } diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 3168411d8..f5d640a69 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -115,6 +115,9 @@ static void test_execute_directory(bool gather_stdout) { assert_se(chmod(masked2e, 0755) == 0); assert_se(chmod(mask2e, 0755) == 0); + if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return; + if (gather_stdout) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); else @@ -183,6 +186,9 @@ static void test_execution_order(void) { assert_se(chmod(override, 0755) == 0); assert_se(chmod(masked, 0755) == 0); + if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(read_full_file(output, &contents, NULL) >= 0); @@ -265,6 +271,9 @@ static void test_stdout_gathering(void) { assert_se(chmod(name2, 0755) == 0); assert_se(chmod(name3, 0755) == 0); + if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return; + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(r >= 0); @@ -331,6 +340,9 @@ static void test_environment_gathering(void) { r = setenv("PATH", "no-sh-built-in-path", 1); assert_se(r >= 0); + if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return; + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(r >= 0); @@ -395,6 +407,9 @@ static void test_error_catching(void) { assert_se(chmod(name2, 0755) == 0); assert_se(chmod(name3, 0755) == 0); + if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return; + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE); /* we should exit with the error code of the first script that failed */ diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 4e0fd7d5b..9ca062021 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -467,7 +467,7 @@ static void test_exec_restrictnamespaces(Manager *m) { test(__func__, m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); test(__func__, m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED); test(__func__, m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); - test(__func__, m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED); test(__func__, m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); test(__func__, m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); test(__func__, m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); @@ -806,7 +806,6 @@ static int run_tests(UnitFileScope scope, const test_entry tests[], char **patte int main(int argc, char *argv[]) { _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - _cleanup_free_ char *test_execute_path = NULL; static const test_entry user_tests[] = { entry(test_exec_basic), @@ -867,6 +866,7 @@ int main(int argc, char *argv[]) { (void) unsetenv("LOGNAME"); (void) unsetenv("SHELL"); (void) unsetenv("HOME"); + (void) unsetenv("TMPDIR"); can_unshare = have_namespaces(); @@ -878,9 +878,10 @@ int main(int argc, char *argv[]) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); + _cleanup_free_ char *unit_dir = NULL; + assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0); + assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - test_execute_path = path_join(get_testdata_dir(), "test-execute"); - assert_se(set_unit_path(test_execute_path) >= 0); /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test * cases, otherwise (and if they are present in the environment), diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 23c7d370d..95dcd4fdb 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -15,6 +15,8 @@ #include "io-util.h" #include "parse-util.h" #include "process-util.h" +#include "rm-rf.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -444,20 +446,23 @@ static void test_write_string_file_verify(void) { _cleanup_free_ char *buf = NULL, *buf2 = NULL; int r; - assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0); + r = read_one_line_file("/proc/version", &buf); + if (ERRNO_IS_PRIVILEGE(r)) + return; + assert_se(r >= 0); assert_se(buf2 = strjoin(buf, "\n")); - r = write_string_file("/proc/cmdline", buf, 0); + r = write_string_file("/proc/version", buf, 0); assert_se(IN_SET(r, -EACCES, -EIO)); - r = write_string_file("/proc/cmdline", buf2, 0); + r = write_string_file("/proc/version", buf2, 0); assert_se(IN_SET(r, -EACCES, -EIO)); - assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); - assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); + assert_se(write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); + assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); - r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE); + r = write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE); assert_se(IN_SET(r, -EACCES, -EIO)); - assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0); + assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0); } static void test_load_env_file_pairs(void) { @@ -630,8 +635,7 @@ static void test_tempfn(void) { static const char chars[] = "Aąę„”\n루\377"; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" +DISABLE_WARNING_TYPE_LIMITS; static void test_fgetc(void) { _cleanup_fclose_ FILE *f = NULL; @@ -662,7 +666,7 @@ static void test_fgetc(void) { assert_se(safe_fgetc(f, &c) == 0); } -#pragma GCC diagnostic pop +REENABLE_WARNING; static const char buffer[] = "Some test data\n" @@ -757,7 +761,7 @@ static void test_read_line3(void) { _cleanup_free_ char *line = NULL; int r; - f = fopen("/proc/cmdline", "re"); + f = fopen("/proc/uptime", "re"); if (!f && IN_SET(errno, ENOENT, EPERM)) return; assert_se(f); @@ -840,6 +844,53 @@ static void test_read_nul_string(void) { assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, "")); } +static void test_read_full_file_socket(void) { + _cleanup_(rm_rf_physical_and_freep) char *z = NULL; + _cleanup_close_ int listener = -1; + _cleanup_free_ char *data = NULL; + union sockaddr_union sa; + const char *j; + size_t size; + pid_t pid; + int r; + + log_info("/* %s */", __func__); + + listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + assert_se(listener >= 0); + + assert_se(mkdtemp_malloc(NULL, &z) >= 0); + j = strjoina(z, "/socket"); + + assert_se(sockaddr_un_set_path(&sa.un, j) >= 0); + + assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0); + assert_se(listen(listener, 1) >= 0); + + r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid); + assert_se(r >= 0); + if (r == 0) { + _cleanup_close_ int rfd = -1; + /* child */ + + rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC); + assert_se(rfd >= 0); + +#define TEST_STR "This is a test\nreally." + + assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR)); + _exit(EXIT_SUCCESS); + } + + assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO); + assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0); + assert_se(size == strlen(TEST_STR)); + assert_se(streq(data, TEST_STR)); + + assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0); +#undef TEST_STR +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -865,6 +916,7 @@ int main(int argc, char *argv[]) { test_read_line3(); test_read_line4(); test_read_nul_string(); + test_read_full_file_socket(); return 0; } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index d97ccfda3..dfea70ca2 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -670,7 +670,7 @@ static void test_unlinkat_deallocate(void) { assert_se(st.st_blocks > 0); assert_se(st.st_nlink == 1); - assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0); + assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0); assert_se(fstat(fd, &st) >= 0); assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6 @@ -846,6 +846,37 @@ static void test_chmod_and_chown_unsafe(void) { assert_se(S_ISLNK(st.st_mode)); } +static void test_path_is_encrypted_one(const char *p, int expect) { + int r; + + r = path_is_encrypted(p); + if (r == -ENOENT) /* This might fail, if btrfs is used and we run in a container. In that case we + * cannot resolve the device node paths that BTRFS_IOC_DEV_INFO returns, because + * the device nodes are unlikely to exist in the container. But if we can't stat() + * them we cannot determine the dev_t of them, and thus cannot figure out if they + * are enrypted. Hence let's just ignore ENOENT here. */ + return; + assert_se(r >= 0); + + log_info("%s encrypted: %s", p, yes_no(r)); + + assert_se(expect < 0 || ((r > 0) == (expect > 0))); +} + +static void test_path_is_encrypted(void) { + int booted = sd_booted(); /* If this is run in build environments such as koji, /dev might be a + * reguar fs. Don't assume too much if not running under systemd. */ + + log_info("/* %s (sd_booted=%d)*/", __func__, booted); + + test_path_is_encrypted_one("/home", -1); + test_path_is_encrypted_one("/var", -1); + test_path_is_encrypted_one("/", -1); + test_path_is_encrypted_one("/proc", false); + test_path_is_encrypted_one("/sys", false); + test_path_is_encrypted_one("/dev", booted > 0 ? false : -1); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_INFO); @@ -864,6 +895,7 @@ int main(int argc, char *argv[]) { test_rename_noreplace(); test_chmod_and_chown(); test_chmod_and_chown_unsafe(); + test_path_is_encrypted(); return 0; } diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index 57cf89ff5..09fe71f20 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -251,7 +251,7 @@ static void test_hashmap_put(void) { log_info("/* %s */", __func__); - assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0); + assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) == 1); assert_se(m); valid_hashmap_put = hashmap_put(m, "key 1", val1); @@ -451,18 +451,20 @@ static void test_hashmap_remove_and_replace(void) { } static void test_hashmap_ensure_allocated(void) { - Hashmap *m; - int valid_hashmap; + _cleanup_hashmap_free_ Hashmap *m = NULL; + int r; log_info("/* %s */", __func__); - m = hashmap_new(&string_hash_ops); + r = hashmap_ensure_allocated(&m, &string_hash_ops); + assert_se(r == 1); - valid_hashmap = hashmap_ensure_allocated(&m, &string_hash_ops); - assert_se(valid_hashmap == 0); + r = hashmap_ensure_allocated(&m, &string_hash_ops); + assert_se(r == 0); - assert_se(m); - hashmap_free(m); + /* different hash ops shouldn't matter at this point */ + r = hashmap_ensure_allocated(&m, &trivial_hash_ops); + assert_se(r == 0); } static void test_hashmap_foreach_key(void) { @@ -557,8 +559,7 @@ static void test_hashmap_foreach(void) { } static void test_hashmap_merge(void) { - Hashmap *m; - Hashmap *n; + Hashmap *m, *n; char *val1, *val2, *val3, *val4, *r; log_info("/* %s */", __func__); @@ -572,8 +573,8 @@ static void test_hashmap_merge(void) { val4 = strdup("my val4"); assert_se(val4); - n = hashmap_new(&string_hash_ops); m = hashmap_new(&string_hash_ops); + n = hashmap_new(&string_hash_ops); hashmap_put(m, "Key 1", val1); hashmap_put(m, "Key 2", val2); @@ -586,8 +587,8 @@ static void test_hashmap_merge(void) { r = hashmap_get(m, "Key 4"); assert_se(r && streq(r, "my val4")); - assert_se(n); assert_se(m); + assert_se(n); hashmap_free(n); hashmap_free_free(m); } diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c index 1a6e8ffa5..94dbbf157 100644 --- a/src/test/test-hashmap.c +++ b/src/test/test-hashmap.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "hashmap.h" +#include "string-util.h" #include "util.h" unsigned custom_counter = 0; @@ -109,6 +110,58 @@ static void test_iterated_cache(void) { assert_se(iterated_cache_free(c) == NULL); } +static void test_hashmap_put_strdup(void) { + _cleanup_hashmap_free_ Hashmap *m = NULL; + char *s; + + /* We don't have ordered_hashmap_put_strdup() yet. If it is added, + * these tests should be moved to test-hashmap-plain.c. */ + + log_info("/* %s */", __func__); + + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 1); + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0); + assert_se(hashmap_put_strdup(&m, "foo", "BAR") == -EEXIST); + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0); + assert_se(hashmap_contains(m, "foo")); + + s = hashmap_get(m, "foo"); + assert_se(streq(s, "bar")); + + assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 1); + assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0); + assert_se(hashmap_put_strdup(&m, "xxx", "BAR") == -EEXIST); + assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0); + assert_se(hashmap_contains(m, "xxx")); + + s = hashmap_get(m, "xxx"); + assert_se(streq(s, "bar")); +} + +static void test_hashmap_put_strdup_null(void) { + _cleanup_hashmap_free_ Hashmap *m = NULL; + char *s; + + log_info("/* %s */", __func__); + + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 1); + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0); + assert_se(hashmap_put_strdup(&m, "foo", NULL) == -EEXIST); + assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0); + assert_se(hashmap_contains(m, "foo")); + + s = hashmap_get(m, "foo"); + assert_se(streq(s, "bar")); + + assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 1); + assert_se(hashmap_put_strdup(&m, "xxx", "bar") == -EEXIST); + assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 0); + assert_se(hashmap_contains(m, "xxx")); + + s = hashmap_get(m, "xxx"); + assert_se(s == NULL); +} + int main(int argc, const char *argv[]) { /* This file tests in test-hashmap-plain.c, and tests in test-hashmap-ordered.c, which is generated * from test-hashmap-plain.c. Hashmap tests should be added to test-hashmap-plain.c, and here only if @@ -127,6 +180,8 @@ int main(int argc, const char *argv[]) { test_trivial_compare_func(); test_string_compare_func(); test_iterated_cache(); + test_hashmap_put_strdup(); + test_hashmap_put_strdup_null(); return 0; } diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index ec34f9cd7..5ab82bba6 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -140,6 +140,23 @@ static void test_read_etc_hostname(void) { unlink(path); } +static void test_hostname_malloc(void) { + _cleanup_free_ char *h = NULL, *l = NULL; + + assert_se(h = gethostname_malloc()); + log_info("hostname_malloc: \"%s\"", h); + + assert_se(l = gethostname_short_malloc()); + log_info("hostname_short_malloc: \"%s\"", l); +} + +static void test_fallback_hostname(void) { + if (!hostname_is_valid(FALLBACK_HOSTNAME, false)) { + log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME); + exit(EXIT_FAILURE); + } +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -147,6 +164,9 @@ int main(int argc, char *argv[]) { test_hostname_is_valid(); test_hostname_cleanup(); test_read_etc_hostname(); + test_hostname_malloc(); + + test_fallback_hostname(); return 0; } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 25498916f..f30916088 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -4,6 +4,7 @@ #include "alloc-util.h" #include "fileio.h" +#include "fs-util.h" #include "install.h" #include "mkdir.h" #include "rm-rf.h" @@ -23,6 +24,7 @@ static void test_basic_mask_and_enable(const char *root) { 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); p = strjoina(root, "/usr/lib/systemd/system/a.service"); assert_se(write_string_file(p, @@ -36,26 +38,26 @@ static void test_basic_mask_and_enable(const char *root) { 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_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_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_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_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_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_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(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/dev/null")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); @@ -74,7 +76,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -83,15 +85,15 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.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); /* 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); @@ -102,15 +104,15 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.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); /* 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); @@ -123,22 +125,22 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.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); /* 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(n_changes == 2); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + 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 == UNIT_FILE_SYMLINK); assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service")); @@ -147,9 +149,25 @@ static void test_basic_mask_and_enable(const char *root) { 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_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.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); + + /* 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(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(unlink(p) == 0); } static void test_linked_units(const char *root) { @@ -196,7 +214,7 @@ static void test_linked_units(const char *root) { p = strjoina(root, "/usr/lib/systemd/system/linked2.service"); assert_se(symlink("/opt/linked2.service", p) >= 0); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service"); + 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); @@ -208,7 +226,7 @@ static void test_linked_units(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/opt/linked.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -219,7 +237,7 @@ static void test_linked_units(const char *root) { assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -229,8 +247,8 @@ static void test_linked_units(const char *root) { /* 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(n_changes == 2); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); - q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + 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 == UNIT_FILE_SYMLINK); assert_se(streq(changes[i].source, "/opt/linked.service")); @@ -251,8 +269,8 @@ static void test_linked_units(const char *root) { /* 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(n_changes == 2); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); - q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + 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 == UNIT_FILE_UNLINK); @@ -271,8 +289,8 @@ static void test_linked_units(const char *root) { assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service"); - q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service"); + 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 == UNIT_FILE_SYMLINK); assert_se(streq(changes[i].source, "/opt/linked2.service")); @@ -325,7 +343,7 @@ static void test_default(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH "/" SPECIAL_DEFAULT_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; @@ -355,7 +373,7 @@ static void test_add_dependency(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/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); changes = NULL; n_changes = 0; @@ -386,7 +404,7 @@ static void test_template_enable(const char *root) { 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_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); @@ -396,7 +414,7 @@ static void test_template_enable(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.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; @@ -404,7 +422,7 @@ static void test_template_enable(const char *root) { 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_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_ENABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); @@ -418,7 +436,7 @@ static void test_template_enable(const char *root) { 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_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); @@ -427,7 +445,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -450,7 +468,7 @@ static void test_template_enable(const char *root) { 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_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); @@ -460,7 +478,7 @@ static void test_template_enable(const char *root) { assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -469,7 +487,7 @@ static void test_template_enable(const char *root) { 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_INDIRECT); + 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); @@ -500,25 +518,25 @@ static void test_indirect(const char *root) { 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_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_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(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_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(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -559,7 +577,7 @@ static void test_preset_and_list(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -570,7 +588,7 @@ static void test_preset_and_list(const char *root) { assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -590,7 +608,7 @@ static void test_preset_and_list(const char *root) { assert_se(n_changes > 0); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service"); for (i = 0; i < n_changes; i++) { @@ -624,7 +642,7 @@ static void test_preset_and_list(const char *root) { got_no = true; assert_se(fl->state == UNIT_FILE_DISABLED); } else - assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT)); + assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT, UNIT_FILE_ALIAS)); } unit_file_list_free(h); @@ -655,7 +673,7 @@ static void test_revert(const char *root) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service"); assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); /* Revert the override file */ @@ -666,7 +684,7 @@ static void test_revert(const char *root) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d/dropin.conf"); assert_se(mkdir_parents(p, 0755) >= 0); assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0); @@ -676,7 +694,7 @@ static void test_revert(const char *root) { assert_se(changes[0].type == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d"); assert_se(changes[1].type == UNIT_FILE_UNLINK); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); @@ -715,7 +733,7 @@ static void test_preset_order(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -777,7 +795,7 @@ static void test_with_dropin(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); @@ -795,7 +813,7 @@ static void test_with_dropin(const char *root) { "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-3.service.d/dropin.conf"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-3.service.d/dropin.conf"); assert_se(mkdir_parents(p, 0755) >= 0); assert_se(write_string_file(p, "[Install]\n" @@ -808,7 +826,7 @@ static void test_with_dropin(const char *root) { "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-4a.service.d/dropin.conf"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-4a.service.d/dropin.conf"); assert_se(mkdir_parents(p, 0755) >= 0); assert_se(write_string_file(p, "[Install]\n" @@ -829,9 +847,9 @@ static void test_with_dropin(const char *root) { assert_se(changes[1].type == 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")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_PATH"/graphical.target.wants/with-dropin-1.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -841,11 +859,11 @@ static void test_with_dropin(const char *root) { assert_se(n_changes == 2); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(changes[1].type == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service")); - assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2.service"); + 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")); + 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_PATH"/graphical.target.wants/with-dropin-2.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -857,9 +875,9 @@ static void test_with_dropin(const char *root) { assert_se(changes[1].type == 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")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_PATH"/graphical.target.wants/with-dropin-3.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -871,9 +889,9 @@ static void test_with_dropin(const char *root) { assert_se(changes[1].type == 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")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-4a.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_PATH"/multi-user.target.wants/with-dropin-4b.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -941,9 +959,9 @@ static void test_with_dropin_template(const char *root) { assert_se(changes[1].type == 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")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_PATH"/graphical.target.wants/with-dropin-1@instance-1.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -954,9 +972,9 @@ static void test_with_dropin_template(const char *root) { assert_se(changes[1].type == 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")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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_PATH"/graphical.target.wants/with-dropin-2@instance-1.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service"); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -965,7 +983,7 @@ static void test_with_dropin_template(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/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; @@ -974,7 +992,7 @@ static void test_with_dropin_template(const char *root) { assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service")); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-3@instance-2.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; @@ -1014,7 +1032,7 @@ static void test_preset_multiple_instances(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -1022,7 +1040,7 @@ static void test_preset_multiple_instances(const char *root) { assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_UNLINK); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -1058,7 +1076,8 @@ static void verify_one( r = unit_file_verify_alias(i, alias, &alias2); log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s", i->name, alias, r, expected, - alias2 ? " [" : "", alias2 ?: "", alias2 ? "]" : ""); + alias2 ? " [" : "", strempty(alias2), + alias2 ? "]" : ""); assert(r == expected); /* This is is test for "instance propagation". This propagation matters mostly for WantedBy= and @@ -1105,8 +1124,8 @@ static void test_verify_alias(void) { verify_one(&bare_template, "foo.target.wants/plain.service", -EXDEV, NULL); verify_one(&bare_template, "foo.target.wants/plain.socket", -EXDEV, NULL); verify_one(&bare_template, "foo.target.wants/plain@.service", -EXDEV, NULL); - /* Name mistmatch: we cannot allow this, because plain@foo.service would be pulled in by foo.taget, - * but would not be resolvable on its own, since systemd doesn't know how to load the fragment. */ + /* Name mismatch: we cannot allow this, because plain@foo.service would be pulled in by foo.target, + * but would not be resolveable on its own, since systemd doesn't know how to load the fragment. */ verify_one(&bare_template, "foo.target.wants/plain@foo.service", -EXDEV, NULL); verify_one(&bare_template, "foo.target.wants/template1@foo.service", 0, NULL); verify_one(&bare_template, "foo.target.wants/service", -EXDEV, NULL); @@ -1116,7 +1135,7 @@ static void test_verify_alias(void) { verify_one(&bare_template, "foo.target.requires/template1@inst.service", 0, NULL); verify_one(&bare_template, "foo.target.requires/service", -EXDEV, NULL); verify_one(&bare_template, "foo.target.conf/plain.service", -EXDEV, NULL); - verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */ + verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */ verify_one(&bare_template, "FOO@inst.target.requires/plain@.service", -EXDEV, NULL); verify_one(&bare_template, "FOO@inst.target.requires/plain@inst.service", -EXDEV, NULL); verify_one(&bare_template, "FOO@.target.requires/template1@.service", 0, NULL); /* instance propagated */ @@ -1170,7 +1189,7 @@ static void test_verify_alias(void) { verify_one(&inst_template, "bar.target.requires/template3@inst.service", 0, NULL); verify_one(&inst_template, "bar.target.requires/service", -EXDEV, NULL); verify_one(&inst_template, "bar.target.conf/plain.service", -EXDEV, NULL); - verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */ + verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */ verify_one(&inst_template, "BAR@inst.target.requires/plain@.service", -EXDEV, NULL); verify_one(&inst_template, "BAR@inst.target.requires/plain@inst.service", -EXDEV, NULL); verify_one(&inst_template, "BAR@.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */ @@ -1214,7 +1233,7 @@ int main(int argc, char *argv[]) { p = strjoina(root, "/usr/lib/systemd/system/"); assert_se(mkdir_p(p, 0755) >= 0); - p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/"); + p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/"); assert_se(mkdir_p(p, 0755) >= 0); p = strjoina(root, "/run/systemd/system/"); diff --git a/src/test/test-journal-importer.c b/src/test/test-journal-importer.c index 7e898735c..488335695 100644 --- a/src/test/test-journal-importer.c +++ b/src/test/test-journal-importer.c @@ -25,7 +25,7 @@ static void test_basic_parsing(void) { _cleanup_free_ char *journal_data_path = NULL; int r; - journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-1.txt"); + assert_se(get_testdata_dir("journal-data/journal-1.txt", &journal_data_path) >= 0); imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC); assert_se(imp.fd >= 0); @@ -56,7 +56,7 @@ static void test_bad_input(void) { _cleanup_free_ char *journal_data_path = NULL; int r; - journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-2.txt"); + assert_se(get_testdata_dir("journal-data/journal-1.txt", &journal_data_path) >= 0); imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC); assert_se(imp.fd >= 0); diff --git a/src/test/test-json.c b/src/test/test-json.c index c72cd7427..032619a42 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -231,10 +231,9 @@ static void test_zeroes(JsonVariant *v) { assert_se(json_variant_integer(w) == 0); assert_se(json_variant_unsigned(w) == 0U); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" + DISABLE_WARNING_FLOAT_EQUAL; assert_se(json_variant_real(w) == 0.0L); -#pragma GCC diagnostic pop + REENABLE_WARNING; assert_se(json_variant_is_integer(w)); assert_se(json_variant_is_unsigned(w)); diff --git a/src/test/test-load-fragment.c b/src/test/test-load-fragment.c index 7de286436..0293d1cd0 100644 --- a/src/test/test-load-fragment.c +++ b/src/test/test-load-fragment.c @@ -27,6 +27,9 @@ #include "tmpfile-util.h" #include "user-util.h" +/* Nontrivial value serves as a placeholder to check that parsing function (didn't) change it */ +#define CGROUP_LIMIT_DUMMY 3 + static int test_unit_file_get_set(void) { int r; Hashmap *h; @@ -773,6 +776,62 @@ static void test_unit_dump_config_items(void) { unit_dump_config_items(stdout); } +static void test_config_parse_memory_limit(void) { + /* int config_parse_memory_limit( + 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) */ + CGroupContext c; + struct limit_test { + const char *limit; + const char *value; + uint64_t *result; + uint64_t expected; + } limit_tests[]= { + { "MemoryMin", "", &c.memory_min, CGROUP_LIMIT_MIN }, + { "MemoryMin", "0", &c.memory_min, CGROUP_LIMIT_MIN }, + { "MemoryMin", "10", &c.memory_min, 10 }, + { "MemoryMin", "infinity", &c.memory_min, CGROUP_LIMIT_MAX }, + { "MemoryLow", "", &c.memory_low, CGROUP_LIMIT_MIN }, + { "MemoryLow", "0", &c.memory_low, CGROUP_LIMIT_MIN }, + { "MemoryLow", "10", &c.memory_low, 10 }, + { "MemoryLow", "infinity", &c.memory_low, CGROUP_LIMIT_MAX }, + { "MemoryHigh", "", &c.memory_high, CGROUP_LIMIT_MAX }, + { "MemoryHigh", "0", &c.memory_high, CGROUP_LIMIT_DUMMY }, + { "MemoryHigh", "10", &c.memory_high, 10 }, + { "MemoryHigh", "infinity", &c.memory_high, CGROUP_LIMIT_MAX }, + { "MemoryMax", "", &c.memory_max, CGROUP_LIMIT_MAX }, + { "MemoryMax", "0", &c.memory_max, CGROUP_LIMIT_DUMMY }, + { "MemoryMax", "10", &c.memory_max, 10 }, + { "MemoryMax", "infinity", &c.memory_max, CGROUP_LIMIT_MAX }, + }; + size_t i; + int r; + + for (i = 0; i < ELEMENTSOF(limit_tests); i++) { + c.memory_min = CGROUP_LIMIT_DUMMY; + c.memory_low = CGROUP_LIMIT_DUMMY; + c.memory_high = CGROUP_LIMIT_DUMMY; + c.memory_max = CGROUP_LIMIT_DUMMY; + r = config_parse_memory_limit(NULL, "fake", 1, "section", 1, + limit_tests[i].limit, 1, + limit_tests[i].value, &c, NULL); + log_info("%s=%s\t%"PRIu64"==%"PRIu64"\n", + limit_tests[i].limit, limit_tests[i].value, + *limit_tests[i].result, limit_tests[i].expected); + assert_se(r >= 0); + assert_se(*limit_tests[i].result == limit_tests[i].expected); + } + +} + int main(int argc, char *argv[]) { _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; int r; @@ -793,6 +852,7 @@ int main(int argc, char *argv[]) { test_config_parse_pass_environ(); TEST_REQ_RUNNING_SYSTEMD(test_install_printf()); test_unit_dump_config_items(); + test_config_parse_memory_limit(); return r; } diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index f49cc6371..347982dd5 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -36,6 +36,28 @@ static void test_locale_is_valid(void) { assert_se(!locale_is_valid("\x01gar\x02 bage\x03")); } +static void test_locale_is_installed(void) { + log_info("/* %s */", __func__); + + /* Always available */ + assert_se(locale_is_installed("POSIX") > 0); + assert_se(locale_is_installed("C") > 0); + + /* Might, or might not be installed. */ + assert_se(locale_is_installed("en_EN.utf8") >= 0); + assert_se(locale_is_installed("fr_FR.utf8") >= 0); + assert_se(locale_is_installed("fr_FR@euro") >= 0); + assert_se(locale_is_installed("fi_FI") >= 0); + + /* Definitely not valid */ + assert_se(locale_is_installed("") == 0); + assert_se(locale_is_installed("/usr/bin/foo") == 0); + assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0); + + /* Definitely not installed */ + assert_se(locale_is_installed("zz_ZZ") == 0); +} + static void test_keymaps(void) { _cleanup_strv_free_ char **kmaps = NULL; char **p; @@ -67,7 +89,7 @@ static void test_keymaps(void) { #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x)) static void dump_special_glyphs(void) { - assert_cc(SPECIAL_GLYPH_DEPRESSED_SMILEY + 1 == _SPECIAL_GLYPH_MAX); + assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX); log_info("/* %s */", __func__); @@ -85,6 +107,7 @@ static void dump_special_glyphs(void) { dump_glyph(SPECIAL_GLYPH_MU); dump_glyph(SPECIAL_GLYPH_CHECK_MARK); dump_glyph(SPECIAL_GLYPH_CROSS_MARK); + dump_glyph(SPECIAL_GLYPH_EXTERNAL_LINK); dump_glyph(SPECIAL_GLYPH_ECSTATIC_SMILEY); dump_glyph(SPECIAL_GLYPH_HAPPY_SMILEY); dump_glyph(SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY); @@ -92,11 +115,14 @@ static void dump_special_glyphs(void) { dump_glyph(SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY); dump_glyph(SPECIAL_GLYPH_UNHAPPY_SMILEY); dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY); + dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY); + dump_glyph(SPECIAL_GLYPH_TOUCH); } int main(int argc, char *argv[]) { test_get_locales(); test_locale_is_valid(); + test_locale_is_installed(); test_keymaps(); dump_special_glyphs(); diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index f2bfc6c62..a7ad48283 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -14,14 +14,25 @@ #include "util.h" #include "virt.h" +static void test_namespace_cleanup_tmpdir(void) { + { + _cleanup_(namespace_cleanup_tmpdirp) char *dir; + assert_se(dir = strdup(RUN_SYSTEMD_EMPTY)); + } + + { + _cleanup_(namespace_cleanup_tmpdirp) char *dir; + assert_se(dir = strdup("/tmp/systemd-test-namespace.XXXXXX")); + assert_se(mkdtemp(dir)); + } +} + static void test_tmpdir(const char *id, const char *A, const char *B) { _cleanup_free_ char *a, *b; struct stat x, y; char *c, *d; assert_se(setup_tmp_dirs(id, &a, &b) == 0); - assert_se(startswith(a, A)); - assert_se(startswith(b, B)); assert_se(stat(a, &x) >= 0); assert_se(stat(b, &y) >= 0); @@ -29,26 +40,27 @@ static void test_tmpdir(const char *id, const char *A, const char *B) { assert_se(S_ISDIR(x.st_mode)); assert_se(S_ISDIR(y.st_mode)); - assert_se((x.st_mode & 01777) == 0700); - assert_se((y.st_mode & 01777) == 0700); + if (!streq(a, RUN_SYSTEMD_EMPTY)) { + assert_se(startswith(a, A)); + assert_se((x.st_mode & 01777) == 0700); + c = strjoina(a, "/tmp"); + assert_se(stat(c, &x) >= 0); + assert_se(S_ISDIR(x.st_mode)); + assert_se((x.st_mode & 01777) == 01777); + assert_se(rmdir(c) >= 0); + assert_se(rmdir(a) >= 0); + } - c = strjoina(a, "/tmp"); - d = strjoina(b, "/tmp"); - - assert_se(stat(c, &x) >= 0); - assert_se(stat(d, &y) >= 0); - - assert_se(S_ISDIR(x.st_mode)); - assert_se(S_ISDIR(y.st_mode)); - - assert_se((x.st_mode & 01777) == 01777); - assert_se((y.st_mode & 01777) == 01777); - - assert_se(rmdir(c) >= 0); - assert_se(rmdir(d) >= 0); - - assert_se(rmdir(a) >= 0); - assert_se(rmdir(b) >= 0); + if (!streq(b, RUN_SYSTEMD_EMPTY)) { + assert_se(startswith(b, B)); + assert_se((y.st_mode & 01777) == 0700); + d = strjoina(b, "/tmp"); + assert_se(stat(d, &y) >= 0); + assert_se(S_ISDIR(y.st_mode)); + assert_se((y.st_mode & 01777) == 01777); + assert_se(rmdir(d) >= 0); + assert_se(rmdir(b) >= 0); + } } static void test_netns(void) { @@ -152,6 +164,13 @@ static void test_protect_kernel_logs(void) { PROTECT_HOME_NO, PROTECT_SYSTEM_NO, 0, + NULL, + 0, + NULL, + NULL, + 0, + NULL, + NULL, 0, NULL); assert_se(r == 0); @@ -173,6 +192,8 @@ int main(int argc, char *argv[]) { test_setup_logging(LOG_INFO); + test_namespace_cleanup_tmpdir(); + if (!have_namespaces()) { log_tests_skipped("Don't have namespace support"); return EXIT_TEST_SKIP; diff --git a/src/test/test-ns.c b/src/test/test-ns.c index cf8b08ba9..cbc41b7a3 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -76,6 +76,13 @@ int main(int argc, char *argv[]) { PROTECT_HOME_NO, PROTECT_SYSTEM_NO, 0, + NULL, + 0, + NULL, + NULL, + 0, + NULL, + NULL, 0, NULL); if (r < 0) { diff --git a/src/test/test-offline-passwd.c b/src/test/test-offline-passwd.c new file mode 100644 index 000000000..5933ec28a --- /dev/null +++ b/src/test/test-offline-passwd.c @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "offline-passwd.h" +#include "user-util.h" +#include "format-util.h" +#include "tests.h" + +static char *arg_root = NULL; + +static void test_resolve_one(const char *name) { + bool relaxed = name || arg_root; + + if (!name) + name = "root"; + + log_info("/* %s(\"%s\") */", __func__, name); + + _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL; + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; + int r; + + r = name_to_uid_offline(arg_root, name, &uid, &uid_cache); + log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid); + assert_se(relaxed || r == 0); + + r = name_to_uid_offline(arg_root, name, &uid, &uid_cache); + log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid); + assert_se(relaxed || r == 0); + + r = name_to_gid_offline(arg_root, name, &gid, &gid_cache); + log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid); + assert_se(relaxed || r == 0); + + r = name_to_gid_offline(arg_root, name, &gid, &gid_cache); + log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid); + assert_se(relaxed || r == 0); +} + +static int parse_argv(int argc, char *argv[]) { + static const struct option options[] = { + { "root", required_argument, NULL, 'r' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "r:", options, NULL)) >= 0) + switch(c) { + case 'r': + arg_root = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 0; +} + +int main(int argc, char **argv) { + int r; + + test_setup_logging(LOG_DEBUG); + + r = parse_argv(argc, argv); + if (r < 0) + return r; + + if (optind >= argc) + test_resolve_one(NULL); + else + while (optind < argc) + test_resolve_one(argv[optind++]); + + return 0; +} diff --git a/src/test/test-ordered-set.c b/src/test/test-ordered-set.c index 0d29fcfad..268c54fcc 100644 --- a/src/test/test-ordered-set.c +++ b/src/test/test-ordered-set.c @@ -7,6 +7,8 @@ #include "strv.h" static void test_set_steal_first(void) { + log_info("/* %s */", __func__); + _cleanup_ordered_set_free_ OrderedSet *m = NULL; int seen[3] = {}; char *val; @@ -42,12 +44,18 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, void, trivial_hash_ static void test_set_free_with_hash_ops(void) { OrderedSet *m; struct Item items[4] = {}; - unsigned i; + + log_info("/* %s */", __func__); assert_se(m = ordered_set_new(&item_hash_ops)); - for (i = 0; i < ELEMENTSOF(items) - 1; i++) + + for (size_t i = 0; i < ELEMENTSOF(items) - 1; i++) assert_se(ordered_set_put(m, items + i) == 1); + for (size_t i = 0; i < ELEMENTSOF(items) - 1; i++) + assert_se(ordered_set_put(m, items + i) == 0); /* We get 0 here, because we use trivial hash + * ops. Also see below... */ + m = ordered_set_free(m); assert_se(items[0].seen == 1); assert_se(items[1].seen == 1); @@ -57,7 +65,9 @@ static void test_set_free_with_hash_ops(void) { static void test_set_put(void) { _cleanup_ordered_set_free_ OrderedSet *m = NULL; - _cleanup_free_ char **t = NULL; + _cleanup_free_ char **t = NULL, *str = NULL; + + log_info("/* %s */", __func__); m = ordered_set_new(&string_hash_ops); assert_se(m); @@ -71,6 +81,10 @@ static void test_set_put(void) { assert_se(ordered_set_put(m, (void*) "333") == 0); assert_se(ordered_set_put(m, (void*) "22") == 0); + assert_se(str = strdup("333")); + assert_se(ordered_set_put(m, str) == -EEXIST); /* ... and we get -EEXIST here, because we use + * non-trivial hash ops. */ + assert_se(t = ordered_set_get_strv(m)); assert_se(streq(t[0], "1")); assert_se(streq(t[1], "22")); @@ -86,6 +100,8 @@ static void test_set_put_string_set(void) { _cleanup_free_ char **final = NULL; /* "just free" because the strings are in the set */ void *t; + log_info("/* %s */", __func__); + m = ordered_set_new(&string_hash_ops); assert_se(m); diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 62ebc9c92..b9111e925 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -67,15 +67,54 @@ static void test_user_and_global_paths(void) { log_info("+ %s", *p); } -static void print_generator_binary_paths(UnitFileScope scope) { - _cleanup_strv_free_ char **paths; +static void test_generator_binary_paths(UnitFileScope scope) { + char template[] = "/tmp/test-path-lookup.XXXXXXX"; + + _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; - log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + assert_se(mkdtemp(template)); - paths = generator_binary_paths(scope); - STRV_FOREACH(dir, paths) + 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); + + log_info("Generators dirs (%s):", scope == UNIT_FILE_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"); + 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"); + 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); + + log_info("Generators dirs (%s):", scope == UNIT_FILE_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"); + STRV_FOREACH(dir, env_gp_with_env) + log_info(" %s", *dir); + + assert_se(strv_equal(gp_with_env, STRV_MAKE(systemd_generator_path))); + assert_se(strv_equal(env_gp_with_env, STRV_MAKE(systemd_env_generator_path))); } int main(int argc, char **argv) { @@ -87,8 +126,8 @@ int main(int argc, char **argv) { test_user_and_global_paths(); - print_generator_binary_paths(UNIT_FILE_SYSTEM); - print_generator_binary_paths(UNIT_FILE_USER); + test_generator_binary_paths(UNIT_FILE_SYSTEM); + test_generator_binary_paths(UNIT_FILE_USER); return EXIT_SUCCESS; } diff --git a/src/test/test-path.c b/src/test/test-path.c index 6ad222b5f..1075f31bc 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -61,148 +61,244 @@ static void shutdown_test(Manager *m) { manager_free(m); } -static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, const char *service_name) { +static Service *service_for_path(Manager *m, Path *path, const char *service_name) { _cleanup_free_ char *tmp = NULL; Unit *service_unit = NULL; - Service *service = NULL; - usec_t ts; - usec_t timeout = 2 * USEC_PER_SEC; assert_se(m); - assert_se(unit); - assert_se(test_path); + assert_se(path); if (!service_name) { - assert_se(tmp = strreplace(unit->id, ".path", ".service")); + assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service")); service_unit = manager_get_unit(m, tmp); } else service_unit = manager_get_unit(m, service_name); assert_se(service_unit); - service = SERVICE(service_unit); - ts = now(CLOCK_MONOTONIC); - /* We process events until the service related to the path has been successfully started */ - while (service->result != SERVICE_SUCCESS || service->state != SERVICE_START) { - usec_t n; - int r; + return SERVICE(service_unit); +} - r = sd_event_run(m->event, 100 * USEC_PER_MSEC); - assert_se(r >= 0); +static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) { + assert_se(m); + assert_se(service); + + usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC; + + while (path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS || + path->state != path_state || service->state != service_state) { + + assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0); printf("%s: state = %s; result = %s \n", - service_unit->id, + UNIT(path)->id, + path_state_to_string(path->state), + path_result_to_string(path->result)); + printf("%s: state = %s; result = %s \n", + UNIT(service)->id, service_state_to_string(service->state), service_result_to_string(service->result)); - /* But we timeout if the service has not been started in the allocated time */ - n = now(CLOCK_MONOTONIC); - if (ts + timeout < n) { - log_error("Test timeout when testing %s", unit->id); + if (now(CLOCK_MONOTONIC) >= end) { + log_error("Test timeout when testing %s", UNIT(path)->id); exit(EXIT_FAILURE); } } - - assert_se(unit_stop(unit) >= 0); - (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); } static void test_path_exists(Manager *m) { const char *test_path = "/tmp/test-path_exists"; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, NULL); + assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); assert_se(touch(test_path) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - check_stop_unlink(m, unit, test_path, NULL); + /* Service restarts if file still exists */ + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(unit_stop(unit) >= 0); } static void test_path_existsglob(Manager *m) { const char *test_path = "/tmp/test-path_existsglobFOOBAR"; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); + assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, NULL); + assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); assert_se(touch(test_path) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - check_stop_unlink(m, unit, test_path, NULL); + /* Service restarts if file still exists */ + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(unit_stop(unit) >= 0); } static void test_path_changed(Manager *m) { const char *test_path = "/tmp/test-path_changed"; FILE *f; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); - assert_se(touch(test_path) >= 0); - assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, NULL); + assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(touch(test_path) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + /* Service does not restart if file still exists */ + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); f = fopen(test_path, "w"); assert_se(f); fclose(f); - check_stop_unlink(m, unit, test_path, NULL); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); + assert_se(unit_stop(unit) >= 0); } static void test_path_modified(Manager *m) { _cleanup_fclose_ FILE *f = NULL; const char *test_path = "/tmp/test-path_modified"; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); - assert_se(touch(test_path) >= 0); - assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, NULL); + assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(touch(test_path) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + /* Service does not restart if file still exists */ + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); f = fopen(test_path, "w"); assert_se(f); fputs("test", f); - check_stop_unlink(m, unit, test_path, NULL); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); + assert_se(unit_stop(unit) >= 0); } static void test_path_unit(Manager *m) { const char *test_path = "/tmp/test-path_unit"; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, "path-mycustomunit.service"); + assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); assert_se(touch(test_path) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - check_stop_unlink(m, unit, test_path, "path-mycustomunit.service"); + assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(unit_stop(unit) >= 0); } static void test_path_directorynotempty(Manager *m) { const char *test_path = "/tmp/test-path_directorynotempty/"; Unit *unit = NULL; + Path *path = NULL; + Service *service = NULL; assert_se(m); + assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0); + + path = PATH(unit); + service = service_for_path(m, path, NULL); + assert_se(access(test_path, F_OK) < 0); - assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0); assert_se(unit_start(unit) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); /* MakeDirectory default to no */ assert_se(access(test_path, F_OK) < 0); assert_se(mkdir_p(test_path, 0755) >= 0); assert_se(touch(strjoina(test_path, "test_file")) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - check_stop_unlink(m, unit, test_path, NULL); + /* Service restarts if directory is still not empty */ + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); + + assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); + assert_se(unit_stop(UNIT(service)) >= 0); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); + + assert_se(unit_stop(unit) >= 0); } static void test_path_makedirectory_directorymode(Manager *m) { @@ -212,9 +308,10 @@ static void test_path_makedirectory_directorymode(Manager *m) { assert_se(m); + assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0); + assert_se(access(test_path, F_OK) < 0); - assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0); assert_se(unit_start(unit) >= 0); /* Check if the directory has been created */ @@ -242,20 +339,19 @@ int main(int argc, char *argv[]) { NULL, }; - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; _cleanup_free_ char *test_path = NULL; - const test_function_t *test = NULL; - Manager *m = NULL; + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; umask(022); test_setup_logging(LOG_INFO); - test_path = path_join(get_testdata_dir(), "test-path"); + assert_se(get_testdata_dir("test-path", &test_path) >= 0); assert_se(set_unit_path(test_path) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - for (test = tests; test && *test; test++) { + for (const test_function_t *test = tests; test && *test; test++) { + Manager *m = NULL; int r; /* We create a clean environment for each test */ diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c index 21d5b44fb..50f66cb97 100644 --- a/src/test/test-prioq.c +++ b/src/test/test-prioq.c @@ -60,7 +60,7 @@ DEFINE_PRIVATE_HASH_OPS(test_hash_ops, struct test, test_hash, test_compare); static void test_struct(void) { _cleanup_(prioq_freep) Prioq *q = NULL; - _cleanup_(set_freep) Set *s = NULL; + _cleanup_set_free_ Set *s = NULL; unsigned previous = 0, i; struct test *t; diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index bdcd1fc4c..4a9b111a2 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -2,6 +2,7 @@ #include "alloc-util.h" #include "env-util.h" +#include "errno-util.h" #include "log.h" #include "macro.h" #include "proc-cmdline.h" @@ -30,7 +31,7 @@ static void test_proc_cmdline_override(void) { log_info("/* %s */", __func__); assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0); - assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=differnt") == 0); + assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=different") == 0); /* First test if the overrides for /proc/cmdline still work */ _cleanup_free_ char *line = NULL, *value = NULL; @@ -250,6 +251,9 @@ static void test_proc_cmdline_key_startswith(void) { int main(void) { test_setup_logging(LOG_INFO); + if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return log_tests_skipped("can't read /proc/cmdline"); + test_proc_cmdline_parse(); test_proc_cmdline_override(); test_proc_cmdline_given(false); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index d78e0544a..b00dd4a98 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -636,7 +636,7 @@ static void test_setpriority_closest(void) { q = getpriority(PRIO_PROCESS, 0); assert_se(errno == 0 && p == q); - /* It should also be possible to to set the nice level to one higher */ + /* It should also be possible to set the nice level to one higher */ if (p < PRIO_MAX-1) { assert_se(setpriority_closest(++p) > 0); @@ -645,7 +645,7 @@ static void test_setpriority_closest(void) { assert_se(errno == 0 && p == q); } - /* It should also be possible to to set the nice level to two higher */ + /* It should also be possible to set the nice level to two higher */ if (p < PRIO_MAX-1) { assert_se(setpriority_closest(++p) > 0); diff --git a/src/test/test-procfs-util.c b/src/test/test-procfs-util.c index 662688e0f..61434578b 100644 --- a/src/test/test-procfs-util.c +++ b/src/test/test-procfs-util.c @@ -2,9 +2,11 @@ #include +#include "errno-util.h" #include "format-util.h" #include "log.h" #include "procfs-util.h" +#include "tests.h" int main(int argc, char *argv[]) { char buf[CONST_MAX(FORMAT_TIMESPAN_MAX, FORMAT_BYTES_MAX)]; @@ -24,7 +26,11 @@ int main(int argc, char *argv[]) { assert_se(procfs_tasks_get_current(&v) >= 0); log_info("Current number of tasks: %" PRIu64, v); - assert_se(procfs_tasks_get_limit(&v) >= 0); + r = procfs_tasks_get_limit(&v); + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) + return log_tests_skipped("can't read /proc/sys/kernel/pid_max"); + + assert_se(r >= 0); log_info("Limit of tasks: %" PRIu64, v); assert_se(v > 0); assert_se(procfs_tasks_set_limit(v) >= 0); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c index 94c431f7e..ad5bc72a4 100644 --- a/src/test/test-random-util.c +++ b/src/test/test-random-util.c @@ -58,6 +58,7 @@ int main(int argc, char **argv) { test_genuine_random_bytes(0); test_genuine_random_bytes(RANDOM_BLOCK); test_genuine_random_bytes(RANDOM_ALLOW_RDRAND); + test_genuine_random_bytes(RANDOM_ALLOW_INSECURE); test_pseudo_random_bytes(); diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index cd4553784..da6d2a21e 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -25,8 +25,11 @@ int main(int argc, char *argv[]) { return log_tests_skipped("cgroupfs not available"); /* prepare the test */ - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); diff --git a/src/test/test-sd-hwdb.c b/src/test/test-sd-hwdb.c index 17ca6a0e2..eb34d8eab 100644 --- a/src/test/test-sd-hwdb.c +++ b/src/test/test-sd-hwdb.c @@ -1,6 +1,7 @@ #include "sd-hwdb.h" #include "alloc-util.h" +#include "errno-util.h" #include "errno.h" #include "tests.h" @@ -12,7 +13,7 @@ static int test_failed_enumerate(void) { log_info("/* %s */", __func__); r = sd_hwdb_new(&hwdb); - if (r == -ENOENT) + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) return r; assert_se(r == 0); diff --git a/src/test/test-sd-path.c b/src/test/test-sd-path.c new file mode 100644 index 000000000..9260db5ac --- /dev/null +++ b/src/test/test-sd-path.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "sd-path.h" + +#include "alloc-util.h" +#include "string-util.h" +#include "strv.h" +#include "tests.h" + +static void test_sd_path_lookup(void) { + log_info("/* %s */", __func__); + + for (uint64_t i = 0; i < _SD_PATH_MAX; i++) { + _cleanup_free_ char *t = NULL, *s = NULL; + int r; + + r = sd_path_lookup(i, NULL, &t); + if (i == SD_PATH_USER_RUNTIME && r == -ENXIO) + continue; + assert_se(r == 0); + assert_se(t); + log_info("%02"PRIu64": \"%s\"", i, t); + + assert_se(sd_path_lookup(i, "suffix", &s) == 0); + assert_se(s); + log_info("%02"PRIu64": \"%s\"", i, s); + assert_se(endswith(s, "/suffix")); + } + + char *tt; + assert_se(sd_path_lookup(_SD_PATH_MAX, NULL, &tt) == -EOPNOTSUPP); +} + +static void test_sd_path_lookup_strv(void) { + log_info("/* %s */", __func__); + + 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); + if (i == SD_PATH_USER_RUNTIME && r == -ENXIO) + continue; + assert_se(r == 0); + assert_se(t); + log_info("%02"PRIu64":", i); + STRV_FOREACH(item, t) + log_debug(" %s", *item); + + assert_se(sd_path_lookup_strv(i, "suffix", &s) == 0); + assert_se(s); + log_info("%02"PRIu64":", i); + STRV_FOREACH(item, s) { + assert_se(endswith(*item, "/suffix")); + log_debug(" %s", *item); + } + } + + char *tt; + assert_se(sd_path_lookup(_SD_PATH_MAX, NULL, &tt) == -EOPNOTSUPP); +} + +int main(void) { + test_setup_logging(LOG_DEBUG); + + test_sd_path_lookup(); + test_sd_path_lookup_strv(); +} diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 67900d85e..eec2779a9 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fileio.h" #include "macro.h" #include "memory-util.h" #include "missing_sched.h" @@ -122,7 +123,7 @@ static void test_filter_sets(void) { if (pid == 0) { /* Child? */ int fd; - /* If we look at the default set (or one that includes it), whitelist instead of blacklist */ + /* If we look at the default set (or one that includes it), allow-list instead of deny-list */ if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE)) r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW, true); else @@ -283,6 +284,7 @@ static void test_restrict_namespace(void) { static void test_protect_sysctl(void) { pid_t pid; + _cleanup_free_ char *seccomp = NULL; log_info("/* %s */", __func__); @@ -301,6 +303,10 @@ static void test_protect_sysctl(void) { return; } + assert_se(get_proc_field("/proc/self/status", "Seccomp", WHITESPACE, &seccomp) == 0); + if (!streq(seccomp, "0")) + log_warning("Warning: seccomp filter detected, results may be unreliable for %s", __func__); + pid = fork(); assert_se(pid >= 0); diff --git a/src/test/test-set.c b/src/test/test-set.c index b4e7a52fd..d3e6de797 100644 --- a/src/test/test-set.c +++ b/src/test/test-set.c @@ -3,6 +3,8 @@ #include "set.h" #include "strv.h" +const bool mempool_use_allowed = VALGRIND; + static void test_set_steal_first(void) { _cleanup_set_free_ Set *m = NULL; int seen[3] = {}; @@ -86,11 +88,78 @@ static void test_set_put(void) { assert_se(strv_length(t) == 3); } +static void test_set_put_strdup(void) { + _cleanup_set_free_ Set *m = NULL; + + assert_se(set_put_strdup(&m, "aaa") == 1); + assert_se(set_put_strdup(&m, "aaa") == 0); + assert_se(set_put_strdup(&m, "bbb") == 1); + assert_se(set_put_strdup(&m, "bbb") == 0); + assert_se(set_put_strdup(&m, "aaa") == 0); + assert_se(set_size(m) == 2); +} + +static void test_set_put_strdupv(void) { + _cleanup_set_free_ Set *m = NULL; + + assert_se(set_put_strdupv(&m, STRV_MAKE("aaa", "aaa", "bbb", "bbb", "aaa")) == 2); + assert_se(set_put_strdupv(&m, STRV_MAKE("aaa", "aaa", "bbb", "bbb", "ccc")) == 1); + assert_se(set_size(m) == 3); +} + +static void test_set_ensure_allocated(void) { + _cleanup_set_free_ Set *m = NULL; + + assert_se(set_ensure_allocated(&m, &string_hash_ops) == 1); + assert_se(set_ensure_allocated(&m, &string_hash_ops) == 0); + assert_se(set_ensure_allocated(&m, NULL) == 0); + assert_se(set_size(m) == 0); +} + +static void test_set_ensure_put(void) { + _cleanup_set_free_ Set *m = NULL; + + assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 1); + assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 0); + assert_se(set_ensure_put(&m, NULL, "a") == 0); + assert_se(set_ensure_put(&m, &string_hash_ops, "b") == 1); + assert_se(set_ensure_put(&m, &string_hash_ops, "b") == 0); + assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 0); + assert_se(set_size(m) == 2); +} + +static void test_set_ensure_consume(void) { + _cleanup_set_free_ Set *m = NULL; + char *s, *t; + + assert_se(s = strdup("a")); + assert_se(set_ensure_consume(&m, &string_hash_ops_free, s) == 1); + + assert_se(t = strdup("a")); + assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0); + + assert_se(t = strdup("a")); + assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0); + + assert_se(t = strdup("b")); + assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 1); + + assert_se(t = strdup("b")); + assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0); + + assert_se(set_size(m) == 2); +} + int main(int argc, const char *argv[]) { test_set_steal_first(); test_set_free_with_destructor(); test_set_free_with_hash_ops(); test_set_put(); + test_set_put_strdup(); + test_set_put_strdupv(); + test_set_ensure_allocated(); + test_set_ensure_put(); + test_set_ensure_consume(); return 0; } diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c index 1020e0cb3..b9d63d6b4 100644 --- a/src/test/test-sizeof.c +++ b/src/test/test-sizeof.c @@ -14,7 +14,7 @@ /* Print information about various types. Useful when diagnosing * gcc diagnostics on an unfamiliar architecture. */ -#pragma GCC diagnostic ignored "-Wtype-limits" +DISABLE_WARNING_TYPE_LIMITS; #define info(t) \ printf("%s → %zu bits%s, %zu byte alignment\n", STRINGIFY(t), \ diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 2e63aace0..8b4fa8264 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -7,6 +7,7 @@ #include #include +#include "efivars.h" #include "errno-util.h" #include "fd-util.h" #include "log.h" @@ -84,7 +85,9 @@ static void test_sleep(void) { log_info("/* %s */", __func__); - log_info("/= configuration =/"); + printf("Secure boot: %sd\n", enable_disable(is_efi_secure_boot())); + + log_info("/= individual sleep modes =/"); log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0)); log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0)); log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0)); @@ -94,7 +97,7 @@ static void test_sleep(void) { log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0)); log_info("Freeze configured: %s", yes_no(can_sleep_state(freeze) > 0)); - log_info("/= running system =/"); + log_info("/= high-level sleep verbs =/"); r = can_sleep("suspend"); log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); r = can_sleep("hibernate"); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index d36caaa71..b007dd627 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -173,6 +173,45 @@ static void test_in_addr_prefix_next(void) { test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL); } +static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) { + union in_addr_union ubefore, uafter, t; + + assert_se(in_addr_from_string(f, before, &ubefore) >= 0); + + t = ubefore; + assert_se((in_addr_prefix_nth(f, &t, pl, nth) > 0) == !!after); + + if (after) { + assert_se(in_addr_from_string(f, after, &uafter) >= 0); + assert_se(in_addr_equal(f, &t, &uafter) > 0); + } +} + +static void test_in_addr_prefix_nth(void) { + log_info("/* %s */", __func__); + + test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0"); + test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 1, "192.168.1.0"); + test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 4, "192.168.4.0"); + test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 25, 1, "192.168.0.128"); + test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 25, 1, "192.168.255.128"); + test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 24, 0, "192.168.255.0"); + test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 32, 1, NULL); + test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 0, 1, NULL); + + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 8, 1, "4500::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 7, 1, "4600::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 1, "4400:0:0:1::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 2, "4400:0:0:2::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 0xbad, "4400:0:0:0bad::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400:0:0:ffff::", 64, 1, "4400:0:1::"); + test_in_addr_prefix_nth_one(AF_INET6, "4400::", 56, ((uint64_t)1<<48) -1, "44ff:ffff:ffff:ff00::"); + test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 255, "ff00::"); + test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 256, NULL); + test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1, NULL); + test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, 1, NULL); +} + static void test_in_addr_to_string_one(int f, const char *addr) { union in_addr_union ua; _cleanup_free_ char *r = NULL; @@ -245,6 +284,56 @@ static void test_in_addr_ifindex_from_string_auto(void) { assert_se(in_addr_ifindex_from_string_auto("fe80::19%thisinterfacecantexist", &family, &ua, &ifindex) == -ENODEV); } +static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) { + int family, ifindex; + union in_addr_union ua; + _cleanup_free_ char *server_name = NULL; + + assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0); + assert_se(streq_ptr(server_name, expected)); +} + +static void test_in_addr_ifindex_name_from_string_auto(void) { + log_info("/* %s */", __func__); + + test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL); + test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com"); + test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL); + test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com"); +} + +static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) { + _cleanup_free_ char *name = NULL, *x = NULL; + union in_addr_union a; + uint16_t p; + int f, i; + + assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0); + assert_se(family == f); + assert_se(port == p); + assert_se(ifindex == i); + assert_se(streq_ptr(server_name, name)); + assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0); + assert_se(streq(str, x)); +} + +static void test_in_addr_port_ifindex_name_from_string_auto(void) { + log_info("/* %s */", __func__); + + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com"); +} + static void test_sockaddr_equal(void) { union sockaddr_union a = { .in.sin_family = AF_INET, @@ -673,9 +762,12 @@ int main(int argc, char *argv[]) { test_in_addr_is_null(); test_in_addr_prefix_intersect(); test_in_addr_prefix_next(); + test_in_addr_prefix_nth(); test_in_addr_to_string(); test_in_addr_ifindex_to_string(); test_in_addr_ifindex_from_string_auto(); + test_in_addr_ifindex_name_from_string_auto(); + test_in_addr_port_ifindex_name_from_string_auto(); test_sockaddr_equal(); diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index a0ffdf6cb..e81b12b41 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -3,6 +3,7 @@ #include "alloc-util.h" #include "log.h" #include "specifier.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -15,6 +16,8 @@ static void test_specifier_escape_one(const char *a, const char *b) { } static void test_specifier_escape(void) { + log_info("/* %s */", __func__); + test_specifier_escape_one(NULL, NULL); test_specifier_escape_one("", ""); test_specifier_escape_one("%", "%%"); @@ -31,12 +34,54 @@ static void test_specifier_escape_strv_one(char **a, char **b) { } static void test_specifier_escape_strv(void) { + log_info("/* %s */", __func__); + test_specifier_escape_strv_one(NULL, NULL); test_specifier_escape_strv_one(STRV_MAKE(NULL), STRV_MAKE(NULL)); test_specifier_escape_strv_one(STRV_MAKE(""), STRV_MAKE("")); test_specifier_escape_strv_one(STRV_MAKE("foo"), STRV_MAKE("foo")); test_specifier_escape_strv_one(STRV_MAKE("%"), STRV_MAKE("%%")); - test_specifier_escape_strv_one(STRV_MAKE("foo", "%", "foo%", "%foo", "foo%foo", "quux", "%%%"), STRV_MAKE("foo", "%%", "foo%%", "%%foo", "foo%%foo", "quux", "%%%%%%")); + test_specifier_escape_strv_one(STRV_MAKE("foo", "%", "foo%", "%foo", "foo%foo", "quux", "%%%"), + STRV_MAKE("foo", "%%", "foo%%", "%%foo", "foo%%foo", "quux", "%%%%%%")); +} + +/* Any specifier functions which don't need an argument. */ +static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + + { 'g', specifier_group_name, NULL }, + { 'G', specifier_group_id, NULL }, + { 'U', specifier_user_id, NULL }, + { 'u', specifier_user_name, NULL }, + { 'h', specifier_user_home, NULL }, + + { 'T', specifier_tmp_dir, NULL }, + { 'V', specifier_var_tmp_dir, NULL }, + {} +}; + +static void test_specifiers(void) { + log_info("/* %s */", __func__); + + for (const Specifier *s = specifier_table; s->specifier; s++) { + char spec[3]; + _cleanup_free_ char *resolved = NULL; + + xsprintf(spec, "%%%c", s->specifier); + + assert_se(specifier_printf(spec, specifier_table, NULL, &resolved) >= 0); + + log_info("%%%c → %s", s->specifier, resolved); + } } int main(int argc, char *argv[]) { @@ -44,6 +89,7 @@ int main(int argc, char *argv[]) { test_specifier_escape(); test_specifier_escape_strv(); + test_specifiers(); return 0; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index e48d419f5..cba5441d4 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -9,12 +9,17 @@ static void test_specifier_printf(void) { static const Specifier table[] = { - { 'a', specifier_string, (char*) "AAAA" }, - { 'b', specifier_string, (char*) "BBBB" }, - { 'm', specifier_machine_id, NULL }, - { 'B', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, + { 'X', specifier_string, (char*) "AAAA" }, + { 'Y', specifier_string, (char*) "BBBB" }, + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, {} }; @@ -23,7 +28,7 @@ static void test_specifier_printf(void) { log_info("/* %s */", __func__); - r = specifier_printf("xxx a=%a b=%b yyy", table, NULL, &w); + r = specifier_printf("xxx a=%X b=%Y yyy", table, NULL, &w); assert_se(r >= 0); assert_se(w); @@ -31,10 +36,15 @@ static void test_specifier_printf(void) { assert_se(streq(w, "xxx a=AAAA b=BBBB yyy")); free(w); - r = specifier_printf("machine=%m, boot=%B, host=%H, version=%v", table, NULL, &w); + r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", table, 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", table, NULL, &w); + if (w) + puts(w); } static void test_str_in_set(void) { @@ -408,9 +418,8 @@ static void test_strv_split_newlines(void) { l = strv_split_newlines(str); assert_se(l); - STRV_FOREACH(s, l) { + STRV_FOREACH(s, l) assert_se(streq(*s, input_table_multiple[i++])); - } } static void test_strv_split_nulstr(void) { @@ -680,7 +689,7 @@ static void test_strv_push_prepend(void) { log_info("/* %s */", __func__); - a = strv_new("foo", "bar", "three"); + assert_se(a = strv_new("foo", "bar", "three")); assert_se(strv_push_prepend(&a, strdup("first")) >= 0); assert_se(streq(a[0], "first")); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 0e563f549..52e651fae 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -88,7 +88,7 @@ static void test_colors(void) { test_one_color("green", ansi_green()); test_one_color("yellow", ansi_yellow()); test_one_color("blue", ansi_blue()); - test_one_color("megenta", ansi_magenta()); + test_one_color("magenta", ansi_magenta()); test_one_color("grey", ansi_grey()); test_one_color("highlight-red", ansi_highlight_red()); test_one_color("highlight-green", ansi_highlight_green()); diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e3b1f6f8c..8826956d1 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -483,6 +483,38 @@ static void test_in_utc_timezone(void) { assert_se(unsetenv("TZ") >= 0); } +static void test_map_clock_usec(void) { + usec_t nowr, x, y, z; + + log_info("/* %s */", __func__); + nowr = now(CLOCK_REALTIME); + + x = nowr; /* right now */ + y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC); + z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME); + /* Converting forth and back will introduce inaccuracies, since we cannot query both clocks atomically, but it should be small. Even on the slowest CI smaller than 1h */ + + assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR); + + assert_se(nowr < USEC_INFINITY - USEC_PER_DAY*7); /* overflow check */ + x = nowr + USEC_PER_DAY*7; /* 1 week from now */ + y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC); + assert_se(y > 0 && y < USEC_INFINITY); + z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME); + assert_se(z > 0 && z < USEC_INFINITY); + assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR); + + assert_se(nowr > USEC_PER_DAY * 7); /* underflow check */ + x = nowr - USEC_PER_DAY*7; /* 1 week ago */ + y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC); + if (y != 0) { /* might underflow if machine is not up long enough for the monotonic clock to be beyond 1w */ + assert_se(y < USEC_INFINITY); + z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME); + assert_se(z > 0 && z < USEC_INFINITY); + assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR); + } +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_INFO); @@ -511,6 +543,7 @@ int main(int argc, char *argv[]) { test_deserialize_dual_timestamp(); test_usec_shift_clock(); test_in_utc_timezone(); + test_map_clock_usec(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); diff --git a/src/test/test-udev.c b/src/test/test-udev.c index e87a8ec03..c0b215dad 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -17,6 +17,7 @@ #include "log.h" #include "main-func.h" #include "mkdir.h" +#include "namespace-util.h" #include "selinux-util.h" #include "signal-util.h" #include "string-util.h" @@ -36,15 +37,13 @@ static int fake_filesystems(void) { { "test/run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true }, { "test/run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true }, }; - unsigned i; + int r; - if (unshare(CLONE_NEWNS) < 0) - return log_error_errno(errno, "Failed to call unshare(): %m"); + r = detach_mount_namespace(); + if (r < 0) + return log_error_errno(r, "Failed to detach mount namespace: %m"); - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - return log_error_errno(errno, "Failed to mount / as private: %m"); - - for (i = 0; i < ELEMENTSOF(fakefss); i++) + for (size_t i = 0; i < ELEMENTSOF(fakefss); i++) if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error); if (!fakefss[i].ignore_mount_error) @@ -82,12 +81,15 @@ static int run(int argc, char *argv[]) { } log_debug("version %s", GIT_VERSION); - mac_selinux_init(); + + r = mac_selinux_init(); + if (r < 0) + return r; action = argv[1]; devpath = argv[2]; - assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0); + assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY) == 0); const char *syspath = strjoina("/sys", devpath); r = device_new_from_synthetic_event(&dev, syspath, action); @@ -122,8 +124,8 @@ static int run(int argc, char *argv[]) { } } - udev_event_execute_rules(event, 3 * USEC_PER_SEC, NULL, rules); - udev_event_execute_run(event, 3 * USEC_PER_SEC); + udev_event_execute_rules(event, 3 * USEC_PER_SEC, SIGKILL, NULL, rules); + udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL); return 0; } diff --git a/src/test/test-umount.c b/src/test/test-umount.c index 6ab5758ed..02852bc08 100644 --- a/src/test/test-umount.c +++ b/src/test/test-umount.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" +#include "errno-util.h" #include "log.h" #include "path-util.h" #include "string-util.h" @@ -15,8 +16,10 @@ static void test_mount_points_list(const char *fname) { log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo"); - if (fname) - fname = testdata_fname = path_join(get_testdata_dir(), fname); + if (fname) { + assert_se(get_testdata_dir(fname, &testdata_fname) >= 0); + fname = testdata_fname; + } LIST_HEAD_INIT(mp_list_head); assert_se(mount_points_list_get(fname, &mp_list_head) >= 0); @@ -34,14 +37,20 @@ static void test_swap_list(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"); - if (fname) - fname = testdata_fname = path_join(get_testdata_dir(), fname); + if (fname) { + assert_se(get_testdata_dir(fname, &testdata_fname) >= 0); + fname = testdata_fname; + } LIST_HEAD_INIT(mp_list_head); - assert_se(swap_list_get(fname, &mp_list_head) >= 0); + r = swap_list_get(fname, &mp_list_head); + if (ERRNO_IS_PRIVILEGE(r)) + return; + assert_se(r >= 0); LIST_FOREACH(mount_point, m, mp_list_head) log_debug("path=%s o=%s f=0x%lx try-ro=%s dev=%u:%u", diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 6e294c72d..0d524f9a5 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -130,6 +130,7 @@ static void test_unit_name_from_path(void) { test_unit_name_from_path_one("///", ".mount", "-.mount", 0); test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL); test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL); + test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -EINVAL); } static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) { @@ -159,6 +160,7 @@ static void test_unit_name_from_path_instance(void) { test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL); test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL); test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0); + test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -EINVAL); } static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) { diff --git a/src/test/test-util.c b/src/test/test-util.c index 76dd72a59..d4a6c8f5c 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -410,6 +410,85 @@ static void test_system_tasks_max_scale(void) { assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX); } +static void test_foreach_pointer(void) { + int a, b, c, *i; + size_t k = 0; + + FOREACH_POINTER(i, &a, &b, &c) { + switch (k) { + + case 0: + assert_se(i == &a); + break; + + case 1: + assert_se(i == &b); + break; + + case 2: + assert_se(i == &c); + break; + + default: + assert_not_reached("unexpected index"); + break; + } + + k++; + } + + assert(k == 3); + + FOREACH_POINTER(i, &b) { + assert(k == 3); + assert(i == &b); + k = 4; + } + + assert(k == 4); + + FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) { + switch (k) { + + case 4: + assert_se(i == NULL); + break; + + case 5: + assert_se(i == &c); + break; + + case 6: + assert_se(i == NULL); + break; + + case 7: + assert_se(i == &b); + break; + + case 8: + assert_se(i == NULL); + break; + + case 9: + assert_se(i == &a); + break; + + case 10: + assert_se(i == NULL); + break; + + default: + assert_not_reached("unexpected index"); + break; + } + + k++; + } + + assert(k == 11); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_INFO); @@ -428,6 +507,7 @@ int main(int argc, char *argv[]) { test_physical_memory_scale(); test_system_tasks_max(); test_system_tasks_max_scale(); + test_foreach_pointer(); return 0; } diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c index bad289767..28ecffb0c 100644 --- a/src/test/test-watch-pid.c +++ b/src/test/test-watch-pid.c @@ -20,7 +20,10 @@ int main(int argc, char *argv[]) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); - assert_se(set_unit_path(get_testdata_dir()) >= 0); + _cleanup_free_ char *unit_dir = NULL; + 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()); assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); diff --git a/src/test/test-xdg-autostart.c b/src/test/test-xdg-autostart.c new file mode 100644 index 000000000..70287b3c5 --- /dev/null +++ b/src/test/test-xdg-autostart.c @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "string-util.h" +#include "strv.h" +#include "tests.h" +#include "tmpfile-util.h" +#include "xdg-autostart-service.h" + +static void test_translate_name(void) { + _cleanup_free_ char *t; + + assert_se(t = xdg_autostart_service_translate_name("a-b.blub.desktop")); + assert_se(streq(t, "app-a\\x2db.blub-autostart.service")); +} + +static void test_xdg_format_exec_start_one(const char *exec, const char *expected) { + _cleanup_free_ char* out = NULL; + + xdg_autostart_format_exec_start(exec, &out); + log_info("In: '%s', out: '%s', expected: '%s'", exec, out, expected); + 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\""); + + /* 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 \";\\\"\"", "/bin/sleep \";\\\"\""); +} + +static const char* const xdg_desktop_file[] = { + "[Desktop Entry]\n" + "Exec\t =\t /bin/sleep 100\n" /* Whitespace Before/After = must be ignored */ + "OnlyShowIn = A;B;\n" + "NotShowIn=C;;D\\\\\\;;E\n", /* "C", "", "D\;", "E" */ + + "[Desktop Entry]\n" + "Exec=a\n" + "Exec=b\n", + + "[Desktop Entry]\n" + "Hidden=\t true\n", +}; + +static void test_xdg_desktop_parse(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; + + log_info("== %s[%i] ==", __func__, i); + + assert_se(fmkostemp_safe(name, "r+", &f) == 0); + assert_se(fwrite(s, strlen(s), 1, f) == 1); + rewind(f); + + assert_se(service = xdg_autostart_service_parse_desktop(name)); + + switch (i) { + case 0: + assert_se(streq(service->exec_string, "/bin/sleep 100")); + assert_se(strv_equal(service->only_show_in, STRV_MAKE("A", "B"))); + assert_se(strv_equal(service->not_show_in, STRV_MAKE("C", "D\\;", "E"))); + assert_se(!service->hidden); + break; + case 1: + /* The second entry is not permissible and will be ignored (and error logged). */ + assert_se(streq(service->exec_string, "a")); + break; + case 2: + assert_se(service->hidden); + break; + } +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_translate_name(); + test_xdg_format_exec_start(); + + for (size_t i = 0; i < ELEMENTSOF(xdg_desktop_file); i++) + test_xdg_desktop_parse(i, xdg_desktop_file[i]); + + return 0; +} diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 849622484..7afc37d9e 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -9,15 +9,17 @@ #include "sd-bus.h" #include "bus-error.h" -#include "bus-util.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-print-properties.h" #include "format-table.h" #include "in-addr-util.h" #include "main-func.h" #include "pager.h" #include "parse-util.h" #include "pretty-print.h" -#include "spawn-polkit-agent.h" #include "sparse-endian.h" +#include "spawn-polkit-agent.h" #include "string-table.h" #include "strv.h" #include "terminal-util.h" @@ -158,7 +160,7 @@ static int print_status_info(const StatusInfo *i) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); if (i->rtc_local) printf("\n%s" @@ -239,14 +241,13 @@ static int set_time(int argc, char **argv, void *userdata) { if (r < 0) return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]); - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetTime", - &error, - NULL, - "xbb", (int64_t) t, relative, interactive); + r = bus_call_method( + bus, + bus_timedate, + "SetTime", + &error, + NULL, + "xbb", (int64_t) t, relative, interactive); if (r < 0) return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r)); @@ -260,14 +261,7 @@ static int set_timezone(int argc, char **argv, void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetTimezone", - &error, - NULL, - "sb", argv[1], arg_ask_password); + r = bus_call_method(bus, bus_timedate, "SetTimezone", &error, NULL, "sb", argv[1], arg_ask_password); if (r < 0) return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r)); @@ -285,14 +279,13 @@ static int set_local_rtc(int argc, char **argv, void *userdata) { if (b < 0) return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]); - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetLocalRTC", - &error, - NULL, - "bbb", b, arg_adjust_system_clock, arg_ask_password); + r = bus_call_method( + bus, + bus_timedate, + "SetLocalRTC", + &error, + NULL, + "bbb", b, arg_adjust_system_clock, arg_ask_password); if (r < 0) return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r)); @@ -310,14 +303,7 @@ static int set_ntp(int argc, char **argv, void *userdata) { if (b < 0) return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]); - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetNTP", - &error, - NULL, - "bb", b, arg_ask_password); + r = bus_call_method(bus, bus_timedate, "SetNTP", &error, NULL, "bb", b, arg_ask_password); if (r < 0) return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r)); @@ -331,14 +317,7 @@ static int list_timezones(int argc, char **argv, void *userdata) { int r; char** zones; - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "ListTimezones", - &error, - &reply, - NULL); + r = bus_call_method(bus, bus_timedate, "ListTimezones", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to request list of time zones: %s", bus_error_message(&error, r)); @@ -386,10 +365,9 @@ static const char * const ntp_leap_table[4] = { [3] = "not synchronized", }; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" +DISABLE_WARNING_TYPE_LIMITS; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t); -#pragma GCC diagnostic pop +REENABLE_WARNING; static int print_ntp_status_info(NTPStatusInfo *i) { char ts[FORMAT_TIMESPAN_MAX], jitter[FORMAT_TIMESPAN_MAX], @@ -431,7 +409,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) { if (r < 0) return table_log_add_error(r); - r = table_add_cell_stringf(table, NULL, "%s (%s)", i->server_address, i->server_name); + r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->server_address), strna(i->server_name)); if (r < 0) return table_log_add_error(r); @@ -455,7 +433,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); return 0; } @@ -464,7 +442,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) { log_error("Invalid NTP response"); r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); return 0; } @@ -548,7 +526,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) { r = table_print(table, NULL); if (r < 0) - log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); return 0; } @@ -686,7 +664,7 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error r = sd_bus_message_read(m, "s", &name); if (r < 0) - return log_error_errno(r, "Failed to read interface name: %m"); + return bus_log_parse_error(r); if (!streq_ptr(name, "org.freedesktop.timesync1.Manager")) return 0; @@ -843,15 +821,7 @@ static int parse_ifindex_bus(sd_bus *bus, const char *str) { return r; assert(r < 0); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "GetLinkByName", - &error, - &reply, - "s", str); + r = bus_call_method(bus, bus_network_mgr, "GetLinkByName", &error, &reply, "s", str); if (r < 0) return log_error_errno(r, "Failed to get ifindex of interfaces %s: %s", str, bus_error_message(&error, r)); @@ -876,13 +846,7 @@ static int verb_ntp_servers(int argc, char **argv, void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "SetLinkNTP"); + r = bus_message_new_method_call(bus, &req, bus_network_mgr, "SetLinkNTP"); if (r < 0) return bus_log_create_error(r); @@ -914,15 +878,7 @@ static int verb_revert(int argc, char **argv, void *userdata) { polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - r = sd_bus_call_method( - bus, - "org.freedesktop.network1", - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - "RevertLinkNTP", - &error, - NULL, - "i", ifindex); + r = bus_call_method(bus, bus_network_mgr, "RevertLinkNTP", &error, NULL, "i", ifindex); if (r < 0) return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r)); @@ -1098,9 +1054,7 @@ static int run(int argc, char *argv[]) { int r; setlocale(LC_ALL, ""); - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) @@ -1108,7 +1062,7 @@ static int run(int argc, char *argv[]) { r = bus_connect_transport(arg_transport, arg_host, false, &bus); if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); + return bus_log_connect_error(r); return timedatectl_main(bus, argc, argv); } diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 5e2fb50d8..c467b8547 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -12,6 +12,10 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-get-properties.h" +#include "bus-locator.h" +#include "bus-log-control-api.h" +#include "bus-map-properties.h" #include "bus-polkit.h" #include "clock-util.h" #include "conf-files.h" @@ -27,6 +31,7 @@ #include "missing_capability.h" #include "path-util.h" #include "selinux-util.h" +#include "service-util.h" #include "signal-util.h" #include "string-util.h" #include "strv.h" @@ -374,7 +379,10 @@ static int context_write_data_local_rtc(Context *c) { } } - mac_selinux_init(); + r = mac_selinux_init(); + if (r < 0) + return r; + return write_string_file_atomic_label("/etc/adjtime", w); } @@ -468,11 +476,9 @@ static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *erro assert(bus); assert(error); - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, start ? "StartUnit" : "StopUnit", error, &reply, @@ -512,11 +518,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error * log_unit_info(u, "%s unit.", enable ? "Enabling" : "Disabling"); if (enable) - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "EnableUnitFiles", error, NULL, @@ -524,11 +528,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error * u->name, false, true); else - r = sd_bus_call_method( + r = bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "DisableUnitFiles", error, NULL, @@ -538,15 +540,7 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error * if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reload", - error, - NULL, - NULL); + r = bus_call_method(bus, bus_systemd_mgr, "Reload", error, NULL, NULL); if (r < 0) return r; @@ -949,12 +943,10 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error u->path = mfree(u->path); if (!c->slot_job_removed) { - r = sd_bus_match_signal_async( + r = bus_match_signal_async( bus, &slot, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", + bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, c); if (r < 0) @@ -1035,6 +1027,7 @@ static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error static const sd_bus_vtable timedate_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0), @@ -1042,14 +1035,53 @@ static const sd_bus_vtable timedate_vtable[] = { SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0), SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0), SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0), - SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListTimezones", NULL, "as", method_list_timezones, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD_WITH_NAMES("SetTime", + "xbb", + SD_BUS_PARAM(usec_utc) + SD_BUS_PARAM(relative) + SD_BUS_PARAM(interactive), + NULL,, + method_set_time, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetTimezone", + "sb", + SD_BUS_PARAM(timezone) + SD_BUS_PARAM(interactive), + NULL,, + method_set_timezone, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetLocalRTC", + "bbb", + SD_BUS_PARAM(local_rtc) + SD_BUS_PARAM(fix_system) + SD_BUS_PARAM(interactive), + NULL,, + method_set_local_rtc, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("SetNTP", + "bb", + SD_BUS_PARAM(use_ntp) + SD_BUS_PARAM(interactive), + NULL,, + method_set_ntp, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("ListTimezones", + NULL,, + "as", + SD_BUS_PARAM(timezones), + method_list_timezones, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, }; +const BusObjectImplementation manager_object = { + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + .vtables = BUS_VTABLES(timedate_vtable), +}; + static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; @@ -1062,9 +1094,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to get system bus connection: %m"); - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c); + r = bus_add_implementation(bus, &manager_object, c); if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); + return r; + + r = bus_log_control_api_register(bus); + if (r < 0) + return r; r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL); if (r < 0) @@ -1087,10 +1123,15 @@ static int run(int argc, char *argv[]) { log_setup_service(); - umask(0022); + r = service_parse_argv("systemd-timedated.service", + "Manage the system clock and timezone and NTP enablement.", + BUS_IMPLEMENTATIONS(&manager_object, + &log_control_object), + argc, argv); + if (r <= 0) + return r; - if (argc != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); + umask(0022); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); diff --git a/src/timesync/timesyncd-bus.c b/src/timesync/timesyncd-bus.c index 5a5896f0b..6effdfc32 100644 --- a/src/timesync/timesyncd-bus.c +++ b/src/timesync/timesyncd-bus.c @@ -3,7 +3,9 @@ #include "sd-bus.h" #include "alloc-util.h" +#include "bus-get-properties.h" #include "bus-internal.h" +#include "bus-log-control-api.h" #include "bus-protocol.h" #include "bus-util.h" #include "in-addr-util.h" @@ -189,6 +191,10 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add manager object vtable: %m"); + r = bus_log_control_api_register(m->bus); + if (r < 0) + return r; + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.timesync1", 0, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to request name: %m"); diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c index a26c2dad7..532d6ea7e 100644 --- a/src/timesync/timesyncd-conf.c +++ b/src/timesync/timesyncd-conf.c @@ -102,11 +102,14 @@ int manager_parse_config_file(Manager *m) { assert(m); - r = config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf", - CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), - "Time\0", - config_item_perf_lookup, timesyncd_gperf_lookup, - CONFIG_PARSE_WARN, m); + r = config_parse_many_nulstr( + PKGSYSCONFDIR "/timesyncd.conf", + CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), + "Time\0", + config_item_perf_lookup, timesyncd_gperf_lookup, + CONFIG_PARSE_WARN, + m, + NULL); if (r < 0) return r; diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 2980f79b1..5570408fa 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -137,11 +137,10 @@ static int manager_send_request(Manager *m) { } /* re-arm timer with increasing timeout, in case the packets never arrive back */ - if (m->retry_interval > 0) { - if (m->retry_interval < m->poll_interval_max_usec) - m->retry_interval *= 2; - } else - m->retry_interval = m->poll_interval_min_usec; + if (m->retry_interval == 0) + m->retry_interval = NTP_RETRY_INTERVAL_MIN_USEC; + else + m->retry_interval = MIN(m->retry_interval * 4/3, NTP_RETRY_INTERVAL_MAX_USEC); r = manager_arm_timer(m, m->retry_interval); if (r < 0) @@ -407,10 +406,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re .iov_base = &ntpmsg, .iov_len = sizeof(ntpmsg), }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct timeval))]; - } control; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timeval))) control; union sockaddr_union server_addr; struct msghdr msghdr = { .msg_iov = &iov, diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h index 97c4e2ff3..d74521c9c 100644 --- a/src/timesync/timesyncd-manager.h +++ b/src/timesync/timesyncd-manager.h @@ -24,6 +24,9 @@ typedef struct Manager Manager; #define NTP_POLL_INTERVAL_MIN_USEC (32 * USEC_PER_SEC) #define NTP_POLL_INTERVAL_MAX_USEC (2048 * USEC_PER_SEC) +#define NTP_RETRY_INTERVAL_MIN_USEC (15 * USEC_PER_SEC) +#define NTP_RETRY_INTERVAL_MAX_USEC (6 * 60 * USEC_PER_SEC) /* 6 minutes */ + struct Manager { sd_bus *bus; sd_event *event; diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index e56e09ca8..4f514d536 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -89,8 +89,8 @@ settime: } static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_message = NULL; const char *user = "systemd-timesync"; uid_t uid, uid_current; gid_t gid; diff --git a/src/tmpfiles/meson.build b/src/tmpfiles/meson.build new file mode 100644 index 000000000..434dcf800 --- /dev/null +++ b/src/tmpfiles/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1+ + +systemd_tmpfiles_sources = [ + 'src/tmpfiles/tmpfiles.c', + 'src/shared/offline-passwd.c', + 'src/shared/offline-passwd.h', +] diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 3325087b7..2404e36bf 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -39,6 +39,7 @@ #include "main-func.h" #include "mkdir.h" #include "mountpoint-util.h" +#include "offline-passwd.h" #include "pager.h" #include "parse-util.h" #include "path-lookup.h" @@ -184,7 +185,13 @@ static const Specifier specifier_table[] = { { 'm', specifier_machine_id_safe, NULL }, { 'b', specifier_boot_id, NULL }, { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, { 'v', specifier_kernel_release, NULL }, + { 'a', specifier_architecture, NULL }, + { 'o', specifier_os_id, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, { 'g', specifier_group_name, NULL }, { 'G', specifier_group_id, NULL }, @@ -244,7 +251,7 @@ static int specifier_directory(char specifier, const void *data, const void *use i = PTR_TO_UINT(data); assert(i < ELEMENTSOF(paths_system)); - return sd_path_home(paths[i].type, paths[i].suffix, ret); + return sd_path_lookup(paths[i].type, paths[i].suffix, ret); } static int log_unresolvable_specifier(const char *filename, unsigned line) { @@ -256,10 +263,11 @@ static int log_unresolvable_specifier(const char *filename, unsigned line) { * not considered as an error so log at LOG_NOTICE only for the first time * and then downgrade this to LOG_DEBUG for the rest. */ - log_full(notified ? LOG_DEBUG : LOG_NOTICE, - "[%s:%u] Failed to resolve specifier: %s, skipping", - filename, line, - arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected"); + log_syntax(NULL, + notified ? LOG_DEBUG : LOG_NOTICE, + filename, line, 0, + "Failed to resolve specifier: %s, skipping", + arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected"); if (!notified) log_notice("All rules containing unresolvable specifiers will be skipped."); @@ -1078,6 +1086,11 @@ static int fd_set_acls(Item *item, int fd, const char *path, const struct stat * if (r > 0) return -r; /* already warned */ + + /* The above procfs paths don't work if /proc is not mounted. */ + if (r == -ENOENT && proc_mounted() == 0) + r = -ENOSYS; + if (r == -EOPNOTSUPP) { log_debug_errno(r, "ACLs not supported by file system at %s", path); return 0; @@ -1252,7 +1265,7 @@ static int path_set_attribute(Item *item, const char *path) { static int write_one_file(Item *i, const char *path) { _cleanup_close_ int fd = -1, dir_fd = -1; char *bn; - int flags, r; + int r; assert(i); assert(path); @@ -1267,15 +1280,19 @@ static int write_one_file(Item *i, const char *path) { bn = basename(path); - flags = O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY; - /* Follows symlinks */ - fd = openat(dir_fd, bn, i->append_or_force ? flags|O_APPEND : flags, i->mode); + fd = openat(dir_fd, bn, + O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY|(i->append_or_force ? O_APPEND : 0), + i->mode); if (fd < 0) { if (errno == ENOENT) { log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); return 0; } + + if (i->allow_failure) + return log_debug_errno(errno, "Failed to open file \"%s\", ignoring: %m", path); + return log_error_errno(errno, "Failed to open file \"%s\": %m", path); } @@ -1612,7 +1629,7 @@ static int create_subvolume(Item *i, const char *path) { log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); else if (r == -EROFS) log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); - else if (r == -ENOPROTOOPT) + else if (r == -ENOTCONN) log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); else if (r < 0) q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); @@ -2388,8 +2405,7 @@ static bool should_include_path(const char *path) { return true; } - /* no matches, so we should include this path only if we - * have no whitelist at all */ + /* no matches, so we should include this path only if we have no allow list at all */ if (strv_isempty(arg_include_prefixes)) return true; @@ -2426,9 +2442,7 @@ static int specifier_expansion_from_arg(Item *i) { case SET_XATTR: case RECURSIVE_SET_XATTR: - assert(i->xattrs); - - STRV_FOREACH (xattr, i->xattrs) { + STRV_FOREACH(xattr, i->xattrs) { r = specifier_printf(*xattr, specifier_table, NULL, &resolved); if (r < 0) return r; @@ -2478,7 +2492,63 @@ static int patch_var_run(const char *fname, unsigned line, char **path) { return 0; } -static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) { +static int find_uid(const char *user, uid_t *ret_uid, Hashmap **cache) { + int r; + + assert(user); + assert(ret_uid); + + /* First: parse as numeric UID string */ + r = parse_uid(user, ret_uid); + if (r >= 0) + return r; + + /* Second: pass to NSS if we are running "online" */ + if (!arg_root) + return get_user_creds(&user, ret_uid, NULL, NULL, NULL, 0); + + /* Third, synthesize "root" unconditionally */ + if (streq(user, "root")) { + *ret_uid = 0; + return 0; + } + + /* Fourth: use fgetpwent() to read /etc/passwd directly, if we are "offline" */ + return name_to_uid_offline(arg_root, user, ret_uid, cache); +} + +static int find_gid(const char *group, gid_t *ret_gid, Hashmap **cache) { + int r; + + assert(group); + assert(ret_gid); + + /* First: parse as numeric GID string */ + r = parse_gid(group, ret_gid); + if (r >= 0) + return r; + + /* Second: pass to NSS if we are running "online" */ + if (!arg_root) + return get_group_creds(&group, ret_gid, 0); + + /* Third, synthesize "root" unconditionally */ + if (streq(group, "root")) { + *ret_gid = 0; + return 0; + } + + /* Fourth: use fgetgrent() to read /etc/group directly, if we are "offline" */ + return name_to_gid_offline(arg_root, group, ret_gid, cache); +} + +static int parse_line( + const char *fname, + unsigned line, + const char *buffer, + bool *invalid_config, + Hashmap **uid_cache, + Hashmap **gid_cache) { _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; _cleanup_(item_free_contents) Item i = {}; @@ -2506,11 +2576,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (IN_SET(r, -EINVAL, -EBADSLT)) /* invalid quoting and such or an unknown specifier */ *invalid_config = true; - return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line); + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to parse line: %m"); } else if (r < 2) { *invalid_config = true; - log_error("[%s:%u] Syntax error.", fname, line); - return -EIO; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Syntax error."); } if (!empty_or_dash(buffer)) { @@ -2521,8 +2590,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (isempty(action)) { *invalid_config = true; - log_error("[%s:%u] Command too short '%s'.", fname, line, action); - return -EINVAL; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Command too short '%s'.", action); } for (pos = 1; action[pos]; pos++) { @@ -2534,15 +2602,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool allow_failure = true; else { *invalid_config = true; - log_error("[%s:%u] Unknown modifiers in command '%s'", - fname, line, action); - return -EINVAL; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action); } } if (boot && !arg_boot) { - log_debug("Ignoring entry %s \"%s\" because --boot is not specified.", - action, path); + log_syntax(NULL, LOG_DEBUG, fname, line, 0, "Ignoring entry %s \"%s\" because --boot is not specified.", action, path); return 0; } @@ -2556,7 +2621,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (r < 0) { if (IN_SET(r, -EINVAL, -EBADSLT)) *invalid_config = true; - return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, path); + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to replace specifiers in '%s': %m", path); } r = patch_var_run(fname, line, &i.path); @@ -2580,7 +2645,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case RELABEL_PATH: case RECURSIVE_RELABEL_PATH: if (i.argument) - log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type); + log_syntax(NULL, LOG_WARNING, fname, line, 0, "%c lines don't take argument fields, ignoring.", i.type); break; @@ -2599,23 +2664,23 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case WRITE_FILE: if (!i.argument) { *invalid_config = true; - log_error("[%s:%u] Write file requires argument.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Write file requires argument."); } break; case COPY_FILES: if (!i.argument) { - i.argument = path_join(arg_root, "/usr/share/factory", i.path); + i.argument = path_join("/usr/share/factory", i.path); if (!i.argument) return log_oom(); } else if (!path_is_absolute(i.argument)) { *invalid_config = true; - log_error("[%s:%u] Source path is not absolute.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument); - } else if (arg_root) { + } + + if (!empty_or_root(arg_root)) { char *p; p = path_join(arg_root, i.argument); @@ -2631,15 +2696,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case CREATE_BLOCK_DEVICE: if (!i.argument) { *invalid_config = true; - log_error("[%s:%u] Device file requires argument.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument."); } r = parse_dev(i.argument, &i.major_minor); if (r < 0) { *invalid_config = true; - log_error_errno(r, "[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, r, "Can't parse device file major/minor '%s'.", i.argument); } break; @@ -2648,8 +2711,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case RECURSIVE_SET_XATTR: if (!i.argument) { *invalid_config = true; - log_error("[%s:%u] Set extended attribute requires argument.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), + "Set extended attribute requires argument."); } r = parse_xattrs_from_arg(&i); if (r < 0) @@ -2660,8 +2723,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case RECURSIVE_SET_ACL: if (!i.argument) { *invalid_config = true; - log_error("[%s:%u] Set ACLs requires argument.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), + "Set ACLs requires argument."); } r = parse_acls_from_arg(&i); if (r < 0) @@ -2672,8 +2735,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool case RECURSIVE_SET_ATTRIBUTE: if (!i.argument) { *invalid_config = true; - log_error("[%s:%u] Set file attribute requires argument.", fname, line); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), + "Set file attribute requires argument."); } r = parse_attribute_from_arg(&i); if (IN_SET(r, -EINVAL, -EBADSLT)) @@ -2683,15 +2746,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool break; default: - log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); *invalid_config = true; - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), + "Unknown command type '%c'.", (char) i.type); } if (!path_is_absolute(i.path)) { - log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); *invalid_config = true; - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), + "Path '%s' not absolute.", i.path); } path_simplify(i.path, false); @@ -2705,11 +2768,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool if (r < 0) { if (IN_SET(r, -EINVAL, -EBADSLT)) *invalid_config = true; - return log_error_errno(r, "[%s:%u] Failed to substitute specifiers in argument: %m", - fname, line); + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m"); } - if (arg_root) { + if (!empty_or_root(arg_root)) { char *p; p = path_join(arg_root, i.path); @@ -2719,25 +2781,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool } if (!empty_or_dash(user)) { - const char *u = user; - - r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); + r = find_uid(user, &i.uid, uid_cache); if (r < 0) { *invalid_config = true; - return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user); + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve user '%s': %m", user); } i.uid_set = true; } if (!empty_or_dash(group)) { - const char *g = group; - - r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING); + r = find_gid(group, &i.gid, gid_cache); if (r < 0) { *invalid_config = true; - log_error("[%s:%u] Unknown group '%s'.", fname, line, group); - return r; + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s'.", group); } i.gid_set = true; @@ -2752,10 +2809,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool mm++; } - if (parse_mode(mm, &m) < 0) { + r = parse_mode(mm, &m); + if (r < 0) { *invalid_config = true; - log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid mode '%s'.", mode); } i.mode = m; @@ -2771,10 +2828,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool a++; } - if (parse_sec(a, &i.age) < 0) { + r = parse_sec(a, &i.age); + if (r < 0) { *invalid_config = true; - log_error("[%s:%u] Invalid age '%s'.", fname, line, age); - return -EBADMSG; + return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", age); } i.age_set = true; @@ -2788,8 +2845,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool for (n = 0; n < existing->n_items; n++) { if (!item_compatible(existing->items + n, &i) && !i.append_or_force) { - log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.", - fname, line, i.path); + log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Duplicate line for path \"%s\", ignoring.", i.path); return 0; } } @@ -2943,7 +2999,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); + r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_root); if (r < 0) return r; break; @@ -2984,6 +3040,7 @@ static int parse_argv(int argc, char *argv[]) { } static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) { + _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL; _cleanup_fclose_ FILE *_f = NULL; Iterator iterator; unsigned v = 0; @@ -3029,7 +3086,7 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe if (IN_SET(*l, 0, '#')) continue; - k = parse_line(fn, v, l, &invalid_line); + k = parse_line(fn, v, l, &invalid_line, &uid_cache, &gid_cache); if (k < 0) { if (invalid_line) /* Allow reporting with a special code if the caller requested this */ @@ -3138,11 +3195,7 @@ static int link_parent(ItemArray *a) { if (!j) j = ordered_hashmap_get(globs, prefix); if (j) { - r = set_ensure_allocated(&j->children, NULL); - if (r < 0) - return log_oom(); - - r = set_put(j->children, a); + r = set_ensure_put(&j->children, NULL, a); if (r < 0) return log_oom(); @@ -3204,7 +3257,9 @@ static int run(int argc, char *argv[]) { umask(0022); - mac_selinux_init(); + r = mac_selinux_init(); + if (r < 0) + return r; items = ordered_hashmap_new(&item_array_hash_ops); globs = ordered_hashmap_new(&item_array_hash_ops); 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 0e33c0b48..4371da478 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -192,7 +192,9 @@ static int process_one_password_file(const char *filename) { r = config_parse(NULL, filename, NULL, NULL, config_item_table_lookup, items, - CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL); + CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, + NULL, + NULL); if (r < 0) return r; @@ -393,6 +395,10 @@ static int process_and_watch_password_files(bool watch) { return -errno; } + if (pollfd[FD_SIGNAL].revents & POLLNVAL || + pollfd[FD_INOTIFY].revents & POLLNVAL) + return -EBADF; + if (pollfd[FD_INOTIFY].revents != 0) (void) flush_fd(notify); diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c index 57389372c..b5e14922a 100644 --- a/src/udev/ata_id/ata_id.c +++ b/src/udev/ata_id/ata_id.c @@ -514,9 +514,8 @@ int main(int argc, char *argv[]) { printf("ID_TYPE=generic\n"); break; } - } else { + } else printf("ID_TYPE=disk\n"); - } printf("ID_BUS=ata\n"); printf("ID_MODEL=%s\n", model); printf("ID_MODEL_ENC=%s\n", model_enc); @@ -524,9 +523,8 @@ int main(int argc, char *argv[]) { if (serial[0] != '\0') { printf("ID_SERIAL=%s_%s\n", model, serial); printf("ID_SERIAL_SHORT=%s\n", serial); - } else { + } else printf("ID_SERIAL=%s\n", model); - } if (id.command_set_1 & (1<<5)) { printf("ID_ATA_WRITE_CACHE=1\n"); diff --git a/src/udev/meson.build b/src/udev/meson.build index 173b10be5..aa23b0709 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -13,10 +13,9 @@ udevadm_sources = files(''' udevadm-trigger.c udevadm-util.c udevadm-util.h + udevd.c '''.split()) -systemd_udevd_sources = files('udevd.c') - libudev_core_sources = ''' udev-ctrl.c udev-ctrl.h diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 43d1c59b9..60a9d21c1 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -59,4 +59,9 @@ Link.OtherChannels, config_parse_channel, 0, Link.CombinedChannels, config_parse_channel, 0, offsetof(link_config, channels) Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise) Link.RxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring) +Link.RxMiniBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring) +Link.RxJumboBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring) Link.TxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring) +Link.RxFlowControl, config_parse_tristate, 0, offsetof(link_config, rx_flow_control) +Link.TxFlowControl, config_parse_tristate, 0, offsetof(link_config, tx_flow_control) +Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(link_config, autoneg_flow_control) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 79f963b04..72ef0c56b 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -2,6 +2,7 @@ #include #include +#include #include "sd-device.h" #include "sd-netlink.h" @@ -20,6 +21,7 @@ #include "netlink-util.h" #include "network-internal.h" #include "parse-util.h" +#include "path-lookup.h" #include "path-util.h" #include "proc-cmdline.h" #include "random-util.h" @@ -148,6 +150,9 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { .duplex = _DUP_INVALID, .port = _NET_DEV_PORT_INVALID, .autonegotiation = -1, + .rx_flow_control = -1, + .tx_flow_control = -1, + .autoneg_flow_control = -1, }; for (i = 0; i < ELEMENTSOF(link->features); i++) @@ -156,7 +161,8 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { r = config_parse(NULL, filename, file, "Match\0Link\0", config_item_perf_lookup, link_config_gperf_lookup, - CONFIG_PARSE_WARN, link); + CONFIG_PARSE_WARN, link, + NULL); if (r < 0) return r; @@ -169,7 +175,7 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { return 0; } - if (!condition_test_list(link->conditions, NULL, NULL, NULL)) { + if (!condition_test_list(link->conditions, environ, NULL, NULL, NULL)) { log_debug("%s: Conditions do not match the system environment, skipping.", filename); return 0; } @@ -323,7 +329,13 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr if (want_random) { log_device_debug(device, "Using random bytes to generate MAC"); - random_bytes(mac->ether_addr_octet, ETH_ALEN); + + /* 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. */ + r = genuine_random_bytes(mac->ether_addr_octet, ETH_ALEN, RANDOM_ALLOW_RDRAND); + if (r < 0) + return log_device_error_errno(device, r, "Failed to acquire random data to generate MAC: %m"); } else { uint64_t result; @@ -346,7 +358,7 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr int link_config_apply(link_config_ctx *ctx, link_config *config, sd_device *device, const char **name) { - _cleanup_strv_free_ char **altnames = NULL; + _cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL; struct ether_addr generated_mac; struct ether_addr *mac = NULL; const char *new_name = NULL; @@ -403,12 +415,18 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, log_warning_errno(r, "Could not set channels of %s: %m", old_name); } - if (config->ring.rx_pending_set || config->ring.tx_pending_set) { + if (config->ring.rx_pending_set || config->ring.rx_mini_pending_set || config->ring.rx_jumbo_pending_set || config->ring.tx_pending_set) { r = ethtool_set_nic_buffer_size(&ctx->ethtool_fd, old_name, &config->ring); if (r < 0) log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name); } + if (config->rx_flow_control >= 0 || config->tx_flow_control >= 0 || config->autoneg_flow_control >= 0) { + r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control); + if (r < 0) + log_warning_errno(r, "Could not set flow control of %s: %m", old_name); + } + r = sd_device_get_ifindex(device, &ifindex); if (r < 0) return log_device_warning_errno(device, r, "Could not find ifindex: %m"); @@ -521,9 +539,17 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (new_name) strv_remove(altnames, new_name); strv_remove(altnames, old_name); + + r = rtnl_get_link_alternative_names(&ctx->rtnl, ifindex, ¤t_altnames); + if (r < 0) + log_debug_errno(r, "Failed to get alternative names on %s, ignoring: %m", old_name); + + char **p; + STRV_FOREACH(p, current_altnames) + strv_remove(altnames, *p); + strv_uniq(altnames); strv_sort(altnames); - r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, altnames); if (r == -EOPNOTSUPP) log_debug_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s, ignoring: %m", old_name); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index a85bd4b46..827ebf436 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -62,6 +62,9 @@ struct link_config { int features[_NET_DEV_FEAT_MAX]; netdev_channels channels; netdev_ring_param ring; + int rx_flow_control; + int tx_flow_control; + int autoneg_flow_control; LIST_FIELDS(link_config, links); }; diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 94c3b232e..bb08da28b 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -111,9 +111,8 @@ static char *get_value(char **buffer) { */ (*buffer)++; end = quote_string; - } else { + } else end = comma_string; - } val = strsep(buffer, end); if (val && end == quote_string) /* diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c index 8e86d2f0d..59ae6c7ad 100644 --- a/src/udev/udev-builtin-hwdb.c +++ b/src/udev/udev-builtin-hwdb.c @@ -47,7 +47,7 @@ int udev_builtin_hwdb_lookup(sd_device *dev, } static const char *modalias_usb(sd_device *dev, char *s, size_t size) { - const char *v, *p; + const char *v, *p, *n = NULL; uint16_t vn, pn; if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0) @@ -58,15 +58,16 @@ static const char *modalias_usb(sd_device *dev, char *s, size_t size) { return NULL; if (safe_atoux16(p, &pn) < 0) return NULL; - snprintf(s, size, "usb:v%04Xp%04X*", vn, pn); + (void) sd_device_get_sysattr_value(dev, "product", &n); + + snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n)); return s; } static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev, const char *subsystem, const char *prefix, const char *filter, bool test) { - sd_device *d; - char s[16]; + char s[LINE_MAX]; bool last = false; int r = 0; @@ -75,7 +76,7 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev, if (!srcdev) srcdev = dev; - for (d = srcdev; d; ) { + for (sd_device *d = srcdev; d; ) { const char *dsubsys, *devtype, *modalias = NULL; if (sd_device_get_subsystem(d, &dsubsys) < 0) @@ -101,6 +102,8 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev, if (!modalias) goto next; + log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias); + r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test); if (r > 0) break; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index 9ff2b0228..215287c11 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -228,9 +228,8 @@ static bool test_pointers(sd_device *dev, is_touchscreen = true; else if (has_joystick_axes_or_buttons) is_joystick = true; - } else if (has_joystick_axes_or_buttons) { + } else if (has_joystick_axes_or_buttons) is_joystick = true; - } if (has_mt_coordinates) { if (stylus_or_pen) diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 169d6ce8f..b3c0ec827 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -449,11 +449,10 @@ static int names_platform(sd_device *dev, struct netnames *names, bool test) { * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" + DISABLE_WARNING_FORMAT_NONLITERAL; if (sscanf(syspath, pattern, vendor, &model, &instance, ðid) != 4) return -EINVAL; -#pragma GCC diagnostic pop + REENABLE_WARNING; if (!in_charset(vendor, validchars)) return -ENOENT; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index ca38f5608..6c020ac0e 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -253,14 +253,20 @@ static sd_device *handle_scsi_iscsi(sd_device *parent, char **path) { return parent; } -static sd_device *handle_scsi_ata(sd_device *parent, char **path) { +static sd_device *handle_scsi_ata(sd_device *parent, char **path, char **compat_path) { sd_device *targetdev, *target_parent; _cleanup_(sd_device_unrefp) sd_device *atadev = NULL; - const char *port_no, *sysname; + const char *port_no, *sysname, *name; + unsigned host, bus, target, lun; assert(parent); assert(path); + if (sd_device_get_sysname(parent, &name) < 0) + return NULL; + if (sscanf(name, "%u:%u:%u:%u", &host, &bus, &target, &lun) != 4) + return NULL; + if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &targetdev) < 0) return NULL; @@ -275,7 +281,17 @@ static sd_device *handle_scsi_ata(sd_device *parent, char **path) { if (sd_device_get_sysattr_value(atadev, "port_no", &port_no) < 0) return NULL; - path_prepend(path, "ata-%s", port_no); + if (bus != 0) + /* Devices behind port multiplier have a bus != 0*/ + path_prepend(path, "ata-%s.%u.0", port_no, bus); + else + /* Master/slave are distinguished by target id */ + path_prepend(path, "ata-%s.%u", port_no, target); + + /* old compatible persistent link for ATA devices */ + if (compat_path) + path_prepend(compat_path, "ata-%s", port_no); + return parent; } @@ -392,7 +408,7 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid return parent; } -static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_parent) { +static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) { const char *devtype, *id, *name; if (sd_device_get_devtype(parent, &devtype) < 0 || @@ -426,7 +442,7 @@ static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_pa } if (strstr(name, "/ata")) - return handle_scsi_ata(parent, path); + return handle_scsi_ata(parent, path, compat_path); if (strstr(name, "/vmbus_")) return handle_scsi_hyperv(parent, path, 37); @@ -520,6 +536,7 @@ static sd_device *handle_ap(sd_device *parent, char **path) { static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { sd_device *parent; _cleanup_free_ char *path = NULL; + _cleanup_free_ char *compat_path = NULL; bool supported_transport = false; bool supported_parent = false; const char *subsystem; @@ -537,7 +554,7 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { } else if (streq(subsys, "scsi_tape")) { handle_scsi_tape(parent, &path); } else if (streq(subsys, "scsi")) { - parent = handle_scsi(parent, &path, &supported_parent); + parent = handle_scsi(parent, &path, &compat_path, &supported_parent); supported_transport = true; } else if (streq(subsys, "cciss")) { parent = handle_cciss(parent, &path); @@ -557,19 +574,27 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { } } else if (streq(subsys, "pci")) { path_prepend(&path, "pci-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "pci-%s", sysname); parent = skip_subsystem(parent, "pci"); supported_parent = true; } else if (streq(subsys, "platform")) { path_prepend(&path, "platform-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "platform-%s", sysname); parent = skip_subsystem(parent, "platform"); supported_transport = true; supported_parent = true; } else if (streq(subsys, "acpi")) { path_prepend(&path, "acpi-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "acpi-%s", sysname); parent = skip_subsystem(parent, "acpi"); supported_parent = true; } else if (streq(subsys, "xen")) { path_prepend(&path, "xen-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "xen-%s", sysname); parent = skip_subsystem(parent, "xen"); supported_parent = true; } else if (streq(subsys, "virtio")) { @@ -577,16 +602,22 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { supported_transport = true; } else if (streq(subsys, "scm")) { path_prepend(&path, "scm-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "scm-%s", sysname); parent = skip_subsystem(parent, "scm"); supported_transport = true; supported_parent = true; } else if (streq(subsys, "ccw")) { path_prepend(&path, "ccw-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "ccw-%s", sysname); parent = skip_subsystem(parent, "ccw"); supported_transport = true; supported_parent = true; } else if (streq(subsys, "ccwgroup")) { path_prepend(&path, "ccwgroup-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "ccwgroup-%s", sysname); parent = skip_subsystem(parent, "ccwgroup"); supported_transport = true; supported_parent = true; @@ -596,6 +627,8 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { supported_parent = true; } else if (streq(subsys, "iucv")) { path_prepend(&path, "iucv-%s", sysname); + if (compat_path) + path_prepend(&compat_path, "iucv-%s", sysname); parent = skip_subsystem(parent, "iucv"); supported_transport = true; supported_parent = true; @@ -604,10 +637,19 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) { path_prepend(&path, "nvme-%s", nsid); + if (compat_path) + path_prepend(&compat_path, "nvme-%s", nsid); parent = skip_subsystem(parent, "nvme"); supported_parent = true; supported_transport = true; } + } else if (streq(subsys, "spi")) { + const char *sysnum; + + if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) { + path_prepend(&path, "cs-%s", sysnum); + parent = skip_subsystem(parent, "spi"); + } } if (!parent) @@ -671,6 +713,14 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); } + /* + * Compatible link generation for ATA devices + * we assign compat_link to the env variable + * ID_PATH_ATA_COMPAT + */ + if (compat_path) + udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path); + return 0; } diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index d067279f3..1e51f22e2 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -189,12 +189,12 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32 _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL; struct udev_ctrl_msg_wire msg_wire; struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire)); - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; struct msghdr smsg = { .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = cred_msg, - .msg_controllen = sizeof(cred_msg), + .msg_control = &control, + .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; struct ucred *cred; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index b10d48828..e1c2baf7f 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -41,6 +41,7 @@ typedef struct Spawn { pid_t pid; usec_t timeout_warn_usec; usec_t timeout_usec; + int timeout_signal; usec_t event_birth_usec; bool accept_failure; int fd_stdout; @@ -599,7 +600,7 @@ static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(spawn); - kill_and_sigcont(spawn->pid, SIGKILL); + kill_and_sigcont(spawn->pid, spawn->timeout_signal); log_device_error(spawn->device, "Spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, @@ -720,6 +721,7 @@ static int spawn_wait(Spawn *spawn) { int udev_event_spawn(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, bool accept_failure, const char *cmd, char *result, size_t ressize) { @@ -796,6 +798,7 @@ int udev_event_spawn(UdevEvent *event, .accept_failure = accept_failure, .timeout_warn_usec = udev_warn_timeout(timeout_usec), .timeout_usec = timeout_usec, + .timeout_signal = timeout_signal, .event_birth_usec = event->birth_usec, .fd_stdout = outpipe[READ_END], .fd_stderr = errpipe[READ_END], @@ -837,11 +840,6 @@ static int rename_netif(UdevEvent *event) { if (r < 0) return log_device_error_errno(dev, r, "Failed to get ifindex: %m"); - r = rtnl_set_link_name(&event->rtnl, ifindex, event->name); - if (r < 0) - return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m", - ifindex, oldname, event->name); - /* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */ r = device_add_property(dev, "ID_RENAMING", "1"); if (r < 0) @@ -851,6 +849,22 @@ static int rename_netif(UdevEvent *event) { if (r < 0) return log_device_warning_errno(dev, r, "Failed to update properties with new name '%s': %m", event->name); + /* Also set ID_RENAMING boolean property to cloned sd_device object and save it to database + * before calling rtnl_set_link_name(). Otherwise, clients (e.g., systemd-networkd) may receive + * RTM_NEWLINK netlink message before the database is updated. */ + r = device_add_property(event->dev_db_clone, "ID_RENAMING", "1"); + if (r < 0) + return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_RENAMING' property: %m"); + + r = device_update_db(event->dev_db_clone); + if (r < 0) + return log_device_debug_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m"); + + r = rtnl_set_link_name(&event->rtnl, ifindex, event->name); + if (r < 0) + return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m", + ifindex, oldname, event->name); + log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, event->name); return 1; @@ -867,8 +881,7 @@ static int update_devnode(UdevEvent *event) { return log_device_error_errno(dev, r, "Failed to get devnum: %m"); /* remove/update possible left-over symlinks from old database entry */ - if (event->dev_db_clone) - (void) udev_node_update_old_links(dev, event->dev_db_clone); + (void) udev_node_update_old_links(dev, event->dev_db_clone); if (!uid_is_valid(event->uid)) { r = device_get_devnode_uid(dev, &event->uid); @@ -899,6 +912,7 @@ static int update_devnode(UdevEvent *event) { static void event_execute_rules_on_remove( UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules) { @@ -920,7 +934,7 @@ static void event_execute_rules_on_remove( if (sd_device_get_devnum(dev, NULL) >= 0) (void) udev_watch_end(dev); - (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list); + (void) udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list); if (sd_device_get_devnum(dev, NULL) >= 0) (void) udev_node_remove(dev); @@ -930,8 +944,7 @@ static int udev_event_on_move(UdevEvent *event) { sd_device *dev = event->dev; int r; - if (event->dev_db_clone && - sd_device_get_devnum(dev, NULL) < 0) { + if (sd_device_get_devnum(dev, NULL) < 0) { r = device_copy_properties(dev, event->dev_db_clone); if (r < 0) log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m"); @@ -947,6 +960,7 @@ static int udev_event_on_move(UdevEvent *event) { int udev_event_execute_rules(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules) { const char *subsystem; @@ -968,7 +982,7 @@ int udev_event_execute_rules(UdevEvent *event, return log_device_error_errno(dev, r, "Failed to get ACTION: %m"); if (action == DEVICE_ACTION_REMOVE) { - event_execute_rules_on_remove(event, timeout_usec, properties_list, rules); + event_execute_rules_on_remove(event, timeout_usec, timeout_signal, properties_list, rules); return 0; } @@ -976,7 +990,7 @@ int udev_event_execute_rules(UdevEvent *event, if (r < 0) return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m"); - if (event->dev_db_clone && sd_device_get_devnum(dev, NULL) >= 0) + if (sd_device_get_devnum(dev, NULL) >= 0) /* Disable watch during event processing. */ (void) udev_watch_end(event->dev_db_clone); @@ -986,7 +1000,7 @@ int udev_event_execute_rules(UdevEvent *event, return r; } - r = udev_rules_apply_to_event(rules, event, timeout_usec, properties_list); + r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list); if (r < 0) return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m"); @@ -1014,12 +1028,10 @@ int udev_event_execute_rules(UdevEvent *event, device_set_is_initialized(dev); - event->dev_db_clone = sd_device_unref(event->dev_db_clone); - return 0; } -void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) { +void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) { const char *command; void *val; Iterator i; @@ -1043,7 +1055,8 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) { } log_device_debug(event->dev, "Running command \"%s\"", command); - r = udev_event_spawn(event, timeout_usec, false, command, NULL, 0); + + r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0); 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 5cb35514c..a0193ffef 100644 --- a/src/udev/udev-event.h +++ b/src/udev/udev-event.h @@ -54,13 +54,15 @@ size_t udev_event_apply_format(UdevEvent *event, int udev_check_format(const char *value, size_t *offset, const char **hint); 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_execute_rules(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules); -void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec); +void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal); static inline usec_t udev_warn_timeout(usec_t timeout_usec) { return DIV_ROUND_UP(timeout_usec, 3); diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index a34b8d694..31a340309 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -47,10 +47,10 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - log_device_error(dev, "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node); - return -EOPNOTSUPP; - } else if (S_ISLNK(stats.st_mode)) { + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) + return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node); + else if (S_ISLNK(stats.st_mode)) { _cleanup_free_ char *buf = NULL; if (readlink_malloc(slink, &buf) >= 0 && diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 5eb358383..c36f032f6 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -78,7 +78,7 @@ typedef enum { TK_M_ATTR, /* string, takes filename through attribute, sd_device_get_sysattr_value(), util_resolve_subsys_kernel(), etc. */ TK_M_SYSCTL, /* string, takes kernel parameter through attribute */ - /* matches parent paramters */ + /* matches parent parameters */ TK_M_PARENTS_KERNEL, /* string */ TK_M_PARENTS_SUBSYSTEM, /* string */ TK_M_PARENTS_DRIVER, /* string */ @@ -595,7 +595,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp if (!is_match) { if (streq(value, "%k")) return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), - "Ignoring NAME=\"%%k\" is ignored, as it breaks kernel supplied names."); + "NAME=\"%%k\" is ignored, as it breaks kernel supplied names."); if (isempty(value)) return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), "Ignoring NAME=\"\", as udev will not delete any device nodes."); @@ -860,7 +860,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp check_value_format_and_warn(rules, key, value, true); r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL); } else { - log_token_debug(rules, "Resolving user name is disabled, ignoring %s=%s", key, value); + log_token_debug(rules, "User name resolution is disabled, ignoring %s=%s", key, value); return 0; } } else if (streq(key, "GROUP")) { @@ -943,7 +943,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp if (op != OP_ASSIGN) return log_token_invalid_op(rules, key); if (FLAGS_SET(rule_line->type, LINE_HAS_GOTO)) { - log_token_warning(rules, "Contains multiple GOTO key, ignoring GOTO=\"%s\".", value); + log_token_warning(rules, "Contains multiple GOTO keys, ignoring GOTO=\"%s\".", value); return 0; } @@ -1174,7 +1174,7 @@ static void rule_resolve_goto(UdevRuleFile *rule_file) { } } -static int parse_file(UdevRules *rules, const char *filename) { +int udev_rules_parse_file(UdevRules *rules, const char *filename) { _cleanup_free_ char *continuation = NULL, *name = NULL; _cleanup_fclose_ FILE *f = NULL; UdevRuleFile *rule_file; @@ -1277,30 +1277,41 @@ static int parse_file(UdevRules *rules, const char *filename) { return 0; } -int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) { - _cleanup_(udev_rules_freep) UdevRules *rules = NULL; - _cleanup_strv_free_ char **files = NULL; - char **f; - int r; - +UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) { assert(resolve_name_timing >= 0 && resolve_name_timing < _RESOLVE_NAME_TIMING_MAX); - rules = new(UdevRules, 1); + UdevRules *rules = new(UdevRules, 1); if (!rules) - return -ENOMEM; + return NULL; *rules = (UdevRules) { .resolve_name_timing = resolve_name_timing, }; + return rules; +} + +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); + if (!rules) + return -ENOMEM; + (void) udev_rules_check_timestamp(rules); r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS); if (r < 0) - return log_error_errno(r, "Failed to enumerate rules files: %m"); + return log_debug_errno(r, "Failed to enumerate rules files: %m"); - STRV_FOREACH(f, files) - (void) parse_file(rules, *f); + STRV_FOREACH(f, files) { + r = udev_rules_parse_file(rules, *f); + if (r < 0) + log_debug_errno(r, "Failed to read rules file %s, ignoring: %m", *f); + } *ret_rules = TAKE_PTR(rules); return 0; @@ -1329,11 +1340,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) { match = isempty(str); break; case MATCH_TYPE_SUBSYSTEM: - NULSTR_FOREACH(i, "subsystem\0class\0bus\0") - if (streq(i, str)) { - match = true; - break; - } + match = STR_IN_SET(str, "subsystem", "class", "bus"); break; case MATCH_TYPE_PLAIN_WITH_EMPTY: if (isempty(str)) { @@ -1523,6 +1530,7 @@ static int udev_rule_apply_token_to_event( sd_device *dev, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list) { UdevRuleToken *token; @@ -1647,7 +1655,7 @@ static int udev_rule_apply_token_to_event( if (r == -ENOENT) return token->op == OP_NOMATCH; if (r < 0) - return log_rule_error_errno(dev, rules, r, "Failed to test the existence of '%s': %m", buf); + return log_rule_error_errno(dev, rules, r, "Failed to test for the existence of '%s': %m", buf); if (stat(buf, &statbuf) < 0) return token->op == OP_NOMATCH; @@ -1665,19 +1673,19 @@ static int udev_rule_apply_token_to_event( (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf); - r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof(result)); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result)); if (r != 0) { if (r < 0) - log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); + log_rule_warning_errno(dev, rules, r, "Failed to execute \"%s\": %m", buf); else /* returned value is positive when program fails */ - log_rule_debug(dev, rules, "Command \"%s\" returned %d (error), ignoring", buf, r); + log_rule_debug(dev, rules, "Command \"%s\" returned %d (error)", buf, r); return token->op == OP_NOMATCH; } delete_trailing_chars(result, "\n"); count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); if (count > 0) - log_rule_debug(dev, rules, "Replaced %zu character(s) from result of '%s'", + log_rule_debug(dev, rules, "Replaced %zu character(s) in result of \"%s\"", count, buf); event->program_result = strdup(result); @@ -1735,7 +1743,7 @@ static int udev_rule_apply_token_to_event( (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, true, buf, result, sizeof result); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); @@ -1817,7 +1825,7 @@ static int udev_rule_apply_token_to_event( case TK_M_IMPORT_CMDLINE: { _cleanup_free_ char *value = NULL; - r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL, &value); + r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_IGNORE_EFI_OPTIONS, &value); if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to read '%s' option from /proc/cmdline: %m", @@ -2165,7 +2173,8 @@ static bool token_is_for_parents(UdevRuleToken *token) { static int udev_rule_apply_parent_token_to_event( UdevRules *rules, - UdevEvent *event) { + UdevEvent *event, + int timeout_signal) { UdevRuleLine *line; UdevRuleToken *head; @@ -2178,7 +2187,7 @@ static int udev_rule_apply_parent_token_to_event( LIST_FOREACH(tokens, line->current_token, head) { if (!token_is_for_parents(line->current_token)) return true; /* All parent tokens match. */ - r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, NULL); + r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, timeout_signal, NULL); if (r < 0) return r; if (r == 0) @@ -2199,6 +2208,7 @@ static int udev_rule_apply_line_to_event( UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRuleLine **next_line) { @@ -2232,7 +2242,7 @@ static int udev_rule_apply_line_to_event( if (parents_done) continue; - r = udev_rule_apply_parent_token_to_event(rules, event); + r = udev_rule_apply_parent_token_to_event(rules, event, timeout_signal); if (r <= 0) return r; @@ -2240,7 +2250,7 @@ static int udev_rule_apply_line_to_event( continue; } - r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, properties_list); + r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, timeout_signal, properties_list); if (r <= 0) return r; } @@ -2255,6 +2265,7 @@ int udev_rules_apply_to_event( UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list) { UdevRuleFile *file; @@ -2267,7 +2278,7 @@ 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) { - r = udev_rule_apply_line_to_event(rules, event, timeout_usec, properties_list, &next_line); + r = udev_rule_apply_line_to_event(rules, event, timeout_usec, timeout_signal, properties_list, &next_line); if (r < 0) return r; } diff --git a/src/udev/udev-rules.h b/src/udev/udev-rules.h index 9fff5da7b..cdb98e8ce 100644 --- a/src/udev/udev-rules.h +++ b/src/udev/udev-rules.h @@ -16,12 +16,15 @@ typedef enum { _ESCAPE_TYPE_INVALID = -1 } UdevRuleEscapeType; -int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing); +int udev_rules_parse_file(UdevRules *rules, const char *filename); +UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing); +int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing); UdevRules *udev_rules_free(UdevRules *rules); DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free); bool udev_rules_check_timestamp(UdevRules *rules); int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list); int udev_rules_apply_static_dev_perms(UdevRules *rules); diff --git a/src/udev/udev.conf b/src/udev/udev.conf index 7deb7715b..07d7f0cd1 100644 --- a/src/udev/udev.conf +++ b/src/udev/udev.conf @@ -7,4 +7,5 @@ #children_max= #exec_delay= #event_timeout=180 +#timeout_signal=SIGKILL #resolve_names=early diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in index 5acbb2d01..7b4f4006b 100644 --- a/src/udev/udev.pc.in +++ b/src/udev/udev.pc.in @@ -2,4 +2,5 @@ Name: udev Description: udev Version: @PROJECT_VERSION@ -udevdir=@udevlibexecdir@ +udev_dir=@udevlibexecdir@ +udevdir=${udev_dir} diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 1debdf2b3..ae6d8caf5 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -17,6 +17,7 @@ #include "device-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "sort-util.h" #include "string-table.h" #include "string-util.h" #include "udev-util.h" @@ -43,22 +44,47 @@ static const char *arg_export_prefix = NULL; static usec_t arg_wait_for_initialization_timeout = 0; static bool skip_attribute(const char *name) { - static const char* const skip[] = { - "uevent", - "dev", - "modalias", - "resource", - "driver", - "subsystem", - "module", - }; - - return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0; + /* Those are either displayed separately or should not be shown at all. */ + return STR_IN_SET(name, + "uevent", + "dev", + "modalias", + "resource", + "driver", + "subsystem", + "module"); } -static void print_all_attributes(sd_device *device, const char *key) { +typedef struct SysAttr { + const char *name; + const char *value; +} SysAttr; + +static int sysattr_compare(const SysAttr *a, const SysAttr *b) { + return strcmp(a->name, b->name); +} + +static int print_all_attributes(sd_device *device, bool is_parent) { + _cleanup_free_ SysAttr *sysattrs = NULL; + size_t n_items = 0, n_allocated = 0; const char *name, *value; + value = NULL; + (void) sd_device_get_devpath(device, &value); + printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value)); + + value = NULL; + (void) sd_device_get_sysname(device, &value); + printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value)); + + value = NULL; + (void) sd_device_get_subsystem(device, &value); + printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value)); + + value = NULL; + (void) sd_device_get_driver(device, &value); + printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value)); + FOREACH_DEVICE_SYSATTR(device, name) { size_t len; @@ -74,19 +100,34 @@ static void print_all_attributes(sd_device *device, const char *key) { /* skip nonprintable attributes */ len = strlen(value); - while (len > 0 && isprint(value[len-1])) + while (len > 0 && isprint((unsigned char) value[len-1])) len--; if (len > 0) continue; - printf(" %s{%s}==\"%s\"\n", key, name, value); + if (!GREEDY_REALLOC(sysattrs, n_allocated, n_items + 1)) + return log_oom(); + + sysattrs[n_items] = (SysAttr) { + .name = name, + .value = value, + }; + n_items++; } + + typesafe_qsort(sysattrs, n_items, sysattr_compare); + + for (size_t i = 0; i < n_items; i++) + printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value); + puts(""); + + return 0; } static int print_device_chain(sd_device *device) { sd_device *child, *parent; - const char *str; + int r; printf("\n" "Udevadm info starts with the device specified by the devpath and then\n" @@ -96,30 +137,14 @@ static int print_device_chain(sd_device *device) { "and the attributes from one single parent device.\n" "\n"); - (void) sd_device_get_devpath(device, &str); - printf(" looking at device '%s':\n", str); - (void) sd_device_get_sysname(device, &str); - printf(" KERNEL==\"%s\"\n", str); - if (sd_device_get_subsystem(device, &str) < 0) - str = ""; - printf(" SUBSYSTEM==\"%s\"\n", str); - if (sd_device_get_driver(device, &str) < 0) - str = ""; - printf(" DRIVER==\"%s\"\n", str); - print_all_attributes(device, "ATTR"); + r = print_all_attributes(device, false); + if (r < 0) + return r; for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) { - (void) sd_device_get_devpath(parent, &str); - printf(" looking at parent device '%s':\n", str); - (void) sd_device_get_sysname(parent, &str); - printf(" KERNELS==\"%s\"\n", str); - if (sd_device_get_subsystem(parent, &str) < 0) - str = ""; - printf(" SUBSYSTEMS==\"%s\"\n", str); - if (sd_device_get_driver(parent, &str) < 0) - str = ""; - printf(" DRIVERS==\"%s\"\n", str); - print_all_attributes(parent, "ATTRS"); + r = print_all_attributes(parent, true); + if (r < 0) + return r; } return 0; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index 2ca98a729..8cd8ed1a1 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -169,24 +169,13 @@ static int parse_argv(int argc, char *argv[]) { subsystem = devtype = NULL; break; } - case 't': { - _cleanup_free_ char *tag = NULL; - - r = set_ensure_allocated(&arg_tag_filter, &string_hash_ops); + case 't': + /* optarg is stored in argv[], so we don't need to copy it */ + r = set_ensure_put(&arg_tag_filter, &string_hash_ops, optarg); if (r < 0) return r; - - tag = strdup(optarg); - if (!tag) - return -ENOMEM; - - r = set_put(arg_tag_filter, tag); - if (r < 0) - return r; - - tag = NULL; break; - } + case 'V': return print_version(); case 'h': @@ -260,7 +249,7 @@ int monitor_main(int argc, char *argv[], void *userdata) { finalize: hashmap_free_free_free(arg_subsystem_filter); - set_free_free(arg_tag_filter); + set_free(arg_tag_filter); return r; } diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 7d7044eac..66c7103d7 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -14,7 +14,10 @@ #include "sd-bus.h" #include "sd-login.h" +#include "sd-messages.h" +#include "bus-util.h" +#include "io-util.h" #include "libudev-util.h" #include "string-util.h" #include "strv.h" @@ -86,54 +89,69 @@ static int parse_argv(int argc, char *argv[]) { static int emit_deprecation_warning(void) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *unit = NULL, *unit_path = NULL; - _cleanup_strv_free_ char **a = NULL, **b = NULL; + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *unit = NULL; int r; r = sd_pid_get_unit(0, &unit); - if (r < 0 || !streq(unit, "systemd-udev-settle.service")) + if (r < 0) { + log_debug_errno(r, "Failed to determine unit we run in, ignoring: %m"); + return 0; + } + + if (!streq(unit, "systemd-udev-settle.service")) return 0; - log_notice("systemd-udev-settle.service is deprecated."); - - r = sd_bus_open_system(&bus); + r = bus_connect_system_systemd(&bus); if (r < 0) - return log_debug_errno(r, "Failed to open system bus, skipping dependency queries: %m"); + log_debug_errno(r, "Failed to open connection to systemd, skipping dependency queries: %m"); + else { + _cleanup_strv_free_ char **b = NULL; + _cleanup_free_ char *unit_path = NULL; - unit_path = unit_dbus_path_from_name("systemd-udev-settle.service"); - if (!unit_path) - return -ENOMEM; + unit_path = unit_dbus_path_from_name("systemd-udev-settle.service"); + if (!unit_path) + return -ENOMEM; - (void) sd_bus_get_property_strv( - bus, - "org.freedesktop.systemd1", - unit_path, - "org.freedesktop.systemd1.Unit", - "WantedBy", - NULL, - &a); + (void) sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.systemd1.Unit", + "WantedBy", + NULL, + &a); - (void) sd_bus_get_property_strv( - bus, - "org.freedesktop.systemd1", - unit_path, - "org.freedesktop.systemd1.Unit", - "RequiredBy", - NULL, - &b); + (void) sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.systemd1.Unit", + "RequiredBy", + NULL, + &b); - r = strv_extend_strv(&a, b, true); - if (r < 0) - return r; + r = strv_extend_strv(&a, b, true); + if (r < 0) + return r; + } - if (!strv_isempty(a)) { + if (strv_isempty(a)) + /* Print a simple message if we cannot determine the dependencies */ + log_notice("systemd-udev-settle.service is deprecated."); + else { + /* Print a longer, structured message if we can acquire the dependencies (this should be the + * common case). This is hooked up with a catalog entry and everything. */ _cleanup_free_ char *t = NULL; t = strv_join(a, ", "); if (!t) return -ENOMEM; - log_notice("Hint: please fix %s not to pull it in.", t); + log_struct(LOG_NOTICE, + "MESSAGE=systemd-udev-settle.service is deprecated. Please fix %s not to pull it in.", t, + "OFFENDING_UNITS=%s", t, + "MESSAGE_ID=" SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR); } return 0; @@ -141,9 +159,8 @@ static int emit_deprecation_warning(void) { int settle_main(int argc, char *argv[], void *userdata) { _cleanup_(udev_queue_unrefp) struct udev_queue *queue = NULL; - struct pollfd pfd; usec_t deadline; - int r; + int r, fd; r = parse_argv(argc, argv); if (r <= 0) @@ -177,17 +194,12 @@ int settle_main(int argc, char *argv[], void *userdata) { if (!queue) return log_error_errno(errno, "Failed to get udev queue: %m"); - r = udev_queue_get_fd(queue); - if (r < 0) { - log_debug_errno(r, "Queue is empty, nothing to watch."); + fd = udev_queue_get_fd(queue); + if (fd < 0) { + log_debug_errno(fd, "Queue is empty, nothing to watch: %m"); return 0; } - pfd = (struct pollfd) { - .events = POLLIN, - .fd = r, - }; - (void) emit_deprecation_warning(); for (;;) { @@ -202,7 +214,10 @@ int settle_main(int argc, char *argv[], void *userdata) { return -ETIMEDOUT; /* wake up when queue becomes empty */ - if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) { + r = fd_wait_for_event(fd, POLLIN, MSEC_PER_SEC); + if (r < 0) + return r; + if (r & POLLIN) { r = udev_queue_flush(queue); if (r < 0) return log_error_errno(r, "Failed to flush queue: %m"); diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 404e3b5a7..5b9b65439 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -123,7 +123,7 @@ int test_main(int argc, char *argv[], void *userdata) { udev_builtin_init(); - r = udev_rules_new(&rules, arg_resolve_name_timing); + r = udev_rules_load(&rules, arg_resolve_name_timing); if (r < 0) { log_error_errno(r, "Failed to read udev rules: %m"); goto out; @@ -143,7 +143,7 @@ int test_main(int argc, char *argv[], void *userdata) { assert_se(sigfillset(&mask) >= 0); assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0); - udev_event_execute_rules(event, 60 * USEC_PER_SEC, NULL, rules); + udev_event_execute_rules(event, 60 * USEC_PER_SEC, SIGKILL, NULL, rules); FOREACH_DEVICE_PROPERTY(dev, key, value) printf("%s=%s\n", key, value); diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 60c68b502..39113d2fa 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -23,7 +23,7 @@ static bool arg_verbose = false; static bool arg_dry_run = false; -static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) { +static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_set) { sd_device *d; int r, ret = 0; @@ -172,7 +172,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_set_free_free_ Set *settle_set = NULL; + _cleanup_set_free_ Set *settle_set = NULL; usec_t ping_timeout_usec = 5 * USEC_PER_SEC; bool settle = false, ping = false; int c, r; @@ -342,7 +342,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { } if (settle) { - settle_set = set_new(&string_hash_ops); + settle_set = set_new(&string_hash_ops_free); if (!settle_set) return log_oom(); @@ -377,7 +377,8 @@ int trigger_main(int argc, char *argv[], void *userdata) { default: assert_not_reached("Unknown device type"); } - r = exec_list(e, action, settle_set); + + r = exec_list(e, action, settle ? &settle_set : NULL); if (r < 0) return r; diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index e6dbb111a..e476f88f0 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -11,6 +11,7 @@ #include "selinux-util.h" #include "string-util.h" #include "udevadm.h" +#include "udevd.h" #include "udev-util.h" #include "verbs.h" #include "util.h" @@ -110,6 +111,9 @@ static int udevadm_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; + if (strstr(program_invocation_short_name, "udevd")) + return run_udevd(argc, argv); + udev_parse_config(); log_parse_environment(); log_open(); @@ -120,7 +124,10 @@ static int run(int argc, char *argv[]) { log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level()); - mac_selinux_init(); + r = mac_selinux_init(); + if (r < 0) + return r; + return udevadm_main(argc, argv); } diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 2ac1cac97..f3236dedf 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -59,6 +59,7 @@ #include "strv.h" #include "strxcpyx.h" #include "syslog-util.h" +#include "udevd.h" #include "udev-builtin.h" #include "udev-ctrl.h" #include "udev-event.h" @@ -74,6 +75,8 @@ static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY; static unsigned arg_children_max = 0; static usec_t arg_exec_delay_usec = 0; static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; +static int arg_timeout_signal = SIGKILL; +static bool arg_blockdev_read_only = false; typedef struct Manager { sd_event *event; @@ -227,7 +230,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(event); assert(event->worker); - kill_and_sigcont(event->worker->pid, SIGKILL); + kill_and_sigcont(event->worker->pid, arg_timeout_signal); event->worker->state = WORKER_KILLED; log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum); @@ -381,6 +384,56 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { return 1; } +static int worker_mark_block_device_read_only(sd_device *dev) { + _cleanup_close_ int fd = -1; + const char *val; + int state = 1, r; + + assert(dev); + + if (!arg_blockdev_read_only) + return 0; + + /* Do this only once, when the block device is new. If the device is later retriggered let's not + * toggle the bit again, so that people can boot up with full read-only mode and then unset the bit + * for specific devices only. */ + if (!device_for_action(dev, DEVICE_ACTION_ADD)) + return 0; + + 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")) + return 0; + + r = sd_device_get_sysname(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get sysname: %m"); + + /* Exclude synthetic devices for now, this is supposed to be a safety feature to avoid modification + * of physical devices, and what sits on top of those doesn't really matter if we don't allow the + * underlying block devices to receive changes. */ + if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram")) + return 0; + + 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"); + + fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) + return log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); + + if (ioctl(fd, BLKROSET, &state) < 0) + return log_device_warning_errno(dev, errno, "Failed to mark block device '%s' read-only: %m", val); + + log_device_info(dev, "Successfully marked block device '%s' read-only.", val); + return 0; +} + static int worker_process_device(Manager *manager, sd_device *dev) { _cleanup_(udev_event_freep) UdevEvent *udev_event = NULL; _cleanup_close_ int fd_lock = -1; @@ -410,12 +463,14 @@ static int worker_process_device(Manager *manager, sd_device *dev) { if (r < 0) return r; + (void) worker_mark_block_device_read_only(dev); + /* apply rules, create node, symlinks */ - r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, manager->properties, manager->rules); + r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_timeout_signal, manager->properties, manager->rules); if (r < 0) return r; - udev_event_execute_run(udev_event, arg_event_timeout_usec); + udev_event_execute_run(udev_event, arg_event_timeout_usec, arg_timeout_signal); if (!manager->rtnl) /* in case rtnl was initialized */ @@ -564,6 +619,14 @@ static void event_run(Manager *manager, struct event *event) { assert(manager); assert(event); + if (DEBUG_LOGGING) { + DeviceAction action; + + r = device_get_action(event->dev, &action); + log_device_debug(event->dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) ready for processing", + event->seqnum, r >= 0 ? device_action_to_string(action) : ""); + } + HASHMAP_FOREACH(worker, manager->workers, i) { if (worker->state != WORKER_IDLE) continue; @@ -775,6 +838,9 @@ static int is_device_busy(Manager *manager, struct event *event) { return false; set_delaying_seqnum: + log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64, + event->seqnum, loop_event->seqnum); + event->delaying_seqnum = loop_event->seqnum; return true; } @@ -859,7 +925,7 @@ static void event_queue_start(Manager *manager) { udev_builtin_init(); if (!manager->rules) { - r = udev_rules_new(&manager->rules, arg_resolve_name_timing); + r = udev_rules_load(&manager->rules, arg_resolve_name_timing); if (r < 0) { log_warning_errno(r, "Failed to read udev rules: %m"); return; @@ -900,19 +966,15 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat .iov_base = &msg, .iov_len = sizeof(msg), }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control = {}; + CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = &control, .msg_controllen = sizeof(control), }; - struct cmsghdr *cmsg; ssize_t size; - struct ucred *ucred = NULL; + struct ucred *ucred; struct worker *worker; size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT); @@ -931,12 +993,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat continue; } - CMSG_FOREACH(cmsg, &msghdr) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) - ucred = (struct ucred*) CMSG_DATA(cmsg); - + ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred); if (!ucred || ucred->pid <= 0) { log_warning("Ignoring worker message without valid PID"); continue; @@ -1413,15 +1470,13 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) { * udev.children_max= events are fully serialized if set to 1 * udev.exec_delay= delay execution of every executed program * udev.event_timeout= seconds to wait before terminating an event + * udev.blockdev_read_only<=bool> mark all block devices read-only when they appear */ static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r = 0; + int r; assert(key); - if (!value) - return 0; - if (proc_cmdline_key_streq(key, "udev.log_priority")) { if (proc_cmdline_value_missing(key, value)) @@ -1452,8 +1507,38 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = parse_sec(value, &arg_exec_delay_usec); - } else if (startswith(key, "udev.")) - log_warning("Unknown udev kernel command line option \"%s\", ignoring", key); + } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = signal_from_string(value); + if (r > 0) + arg_timeout_signal = r; + + } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { + + if (!value) + arg_blockdev_read_only = true; + else { + r = parse_boolean(value); + if (r < 0) + log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value); + else + arg_blockdev_read_only = r; + } + + if (arg_blockdev_read_only) + log_notice("All physical block devices will be marked read-only."); + + return 0; + + } else { + if (startswith(key, "udev.")) + log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); + + return 0; + } if (r < 0) log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); @@ -1470,7 +1555,7 @@ static int help(void) { return log_oom(); printf("%s [OPTIONS...]\n\n" - "Manages devices.\n\n" + "Rule-based manager for device events and files.\n\n" " -h --help Print this message\n" " -V --version Print version of the program\n" " -d --daemon Detach and run in the background\n" @@ -1489,15 +1574,20 @@ static int help(void) { } static int parse_argv(int argc, char *argv[]) { + enum { + ARG_TIMEOUT_SIGNAL, + }; + static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "event-timeout", required_argument, NULL, 't' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, + { "daemon", no_argument, NULL, 'd' }, + { "debug", no_argument, NULL, 'D' }, + { "children-max", required_argument, NULL, 'c' }, + { "exec-delay", required_argument, NULL, 'e' }, + { "event-timeout", required_argument, NULL, 't' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL }, {} }; @@ -1522,6 +1612,14 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg); break; + case ARG_TIMEOUT_SIGNAL: + r = signal_from_string(optarg); + if (r <= 0) + log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg); + else + arg_timeout_signal = r; + + break; case 't': r = parse_sec(optarg, &arg_event_timeout_usec); if (r < 0) @@ -1689,7 +1787,7 @@ static int main_loop(Manager *manager) { udev_builtin_init(); - r = udev_rules_new(&manager->rules, arg_resolve_name_timing); + r = udev_rules_load(&manager->rules, arg_resolve_name_timing); if (!manager->rules) return log_error_errno(r, "Failed to read udev rules: %m"); @@ -1711,7 +1809,7 @@ static int main_loop(Manager *manager) { return r; } -static int run(int argc, char *argv[]) { +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; @@ -1719,7 +1817,7 @@ static int run(int argc, char *argv[]) { log_set_target(LOG_TARGET_AUTO); log_open(); - udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing); + udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing, &arg_timeout_signal); log_parse_environment(); log_open(); /* Done again to update after reading configuration. */ @@ -1743,12 +1841,13 @@ static int run(int argc, char *argv[]) { return r; if (arg_children_max == 0) { - unsigned long cpu_limit, mem_limit; - unsigned long cpu_count = 1; - cpu_set_t cpu_set; + unsigned long cpu_limit, mem_limit, cpu_count = 1; - if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0) - cpu_count = CPU_COUNT(&cpu_set); + r = cpus_in_affinity_mask(); + if (r < 0) + log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m"); + else + cpu_count = r; cpu_limit = cpu_count * 2 + 16; mem_limit = MAX(physical_memory() / (128UL*1024*1024), 10U); @@ -1760,22 +1859,16 @@ static int run(int argc, char *argv[]) { } /* set umask before creating any file/directory */ - r = chdir("/"); - if (r < 0) - return log_error_errno(errno, "Failed to change dir to '/': %m"); - umask(022); r = mac_selinux_init(); if (r < 0) - return log_error_errno(r, "Could not initialize labelling: %m"); + return r; r = mkdir_errno_wrapper("/run/udev", 0755); if (r < 0 && r != -EEXIST) return log_error_errno(r, "Failed to create /run/udev: %m"); - dev_setup(NULL, UID_INVALID, GID_INVALID); - 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 @@ -1818,13 +1911,7 @@ static int run(int argc, char *argv[]) { /* child */ (void) setsid(); - - r = set_oom_score_adjust(-1000); - if (r < 0) - log_debug_errno(r, "Failed to adjust OOM score, ignoring: %m"); } return main_loop(manager); } - -DEFINE_MAIN_FUNCTION(run); diff --git a/src/udev/udevd.h b/src/udev/udevd.h new file mode 100644 index 000000000..848ffc245 --- /dev/null +++ b/src/udev/udevd.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#pragma once + +int run_udevd(int argc, char *argv[]); diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index bbd14165d..c001802dc 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -49,10 +49,8 @@ int main(int argc, char *argv[]) { } r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); + if (r < 0) return EXIT_FAILURE; - } r = apply_timestamp("/etc/.updated", &st.st_mtim); q = apply_timestamp("/var/.updated", &st.st_mtim); diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c index 55fd8ba46..47354d501 100644 --- a/src/update-utmp/update-utmp.c +++ b/src/update-utmp/update-utmp.c @@ -84,11 +84,10 @@ static int get_current_runlevel(Context *c) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; - unsigned i; assert(c); - for (i = 0; i < ELEMENTSOF(table); i++) { + for (size_t i = 0; i < ELEMENTSOF(table); i++) { _cleanup_free_ char *state = NULL, *path = NULL; path = unit_dbus_path_from_name(table[i].special); @@ -125,9 +124,8 @@ static int on_reboot(Context *c) { #if HAVE_AUDIT if (c->audit_fd >= 0) if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { + errno != EPERM) r = log_error_errno(errno, "Failed to send audit message: %m"); - } #endif /* If this call fails it will return 0, which @@ -154,9 +152,8 @@ static int on_shutdown(Context *c) { #if HAVE_AUDIT if (c->audit_fd >= 0) if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { + errno != EPERM) r = log_error_errno(errno, "Failed to send audit message: %m"); - } #endif q = utmp_put_shutdown(); @@ -225,9 +222,6 @@ static int run(int argc, char *argv[]) { }; int r; - if (getppid() != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "This program should be invoked by init only."); if (argc != 2) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires one argument."); diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c index c24142951..cd92b696c 100644 --- a/src/user-sessions/user-sessions.c +++ b/src/user-sessions/user-sessions.c @@ -25,7 +25,9 @@ static int run(int argc, char *argv[]) { umask(0022); - mac_selinux_init(); + r = mac_selinux_init(); + if (r < 0) + return r; if (streq(argv[1], "start")) { r = unlink_or_warn("/run/nologin"); diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index 39dff013a..c973ee9c0 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -85,7 +85,7 @@ static int show_user(UserRecord *ur, Table *table) { TABLE_STRING, user_record_shell(ur), TABLE_INT, (int) user_record_disposition(ur)); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); break; @@ -180,7 +180,7 @@ static int display_user(int argc, char *argv[], void *userdata) { if (table) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } return ret; @@ -234,12 +234,12 @@ static int show_group(GroupRecord *gr, Table *table) { TABLE_GID, gr->gid, TABLE_INT, (int) group_record_disposition(gr)); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); break; default: - assert_not_reached("Unexpected disply mode"); + assert_not_reached("Unexpected display mode"); } return 0; @@ -330,7 +330,7 @@ static int display_group(int argc, char *argv[], void *userdata) { if (table) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } return ret; @@ -377,7 +377,7 @@ static int show_membership(const char *user, const char *group, Table *table) { TABLE_STRING, user, TABLE_STRING, group); if (r < 0) - return log_error_errno(r, "Failed to add row to table: %m"); + return table_log_add_error(r); break; @@ -463,7 +463,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) { if (table) { r = table_print(table, NULL); if (r < 0) - return log_error_errno(r, "Failed to show table: %m"); + return table_log_print_error(r); } return ret; @@ -521,7 +521,7 @@ static int display_services(int argc, char *argv[], void *userdata) { TABLE_STRING, no ?: "yes", TABLE_SET_COLOR, no ? ansi_highlight_red() : ansi_highlight_green()); if (r < 0) - return log_error_errno(r, "Failed to add table row: %m"); + return table_log_add_error(r); } if (table_get_rows(t) <= 0) { @@ -761,9 +761,7 @@ static int run(int argc, char *argv[]) { int r; - log_show_color(true); - log_parse_environment(); - log_open(); + log_setup_cli(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/userdb/userdbd.c b/src/userdb/userdbd.c index 978fd1d78..dbc285e61 100644 --- a/src/userdb/userdbd.c +++ b/src/userdb/userdbd.c @@ -20,8 +20,8 @@ */ static int run(int argc, char *argv[]) { - _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; int r; log_setup_service(); diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c index 3bc5ecc1d..d7202099b 100644 --- a/src/userdb/userwork.c +++ b/src/userdb/userwork.c @@ -9,6 +9,7 @@ #include "fd-util.h" #include "group-record-nss.h" #include "group-record.h" +#include "io-util.h" #include "main-func.h" #include "process-util.h" #include "strv.h" @@ -137,9 +138,9 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) { if (uid_is_valid(p.uid)) - r = nss_user_record_by_uid(p.uid, &hr); + r = nss_user_record_by_uid(p.uid, true, &hr); else if (p.user_name) - r = nss_user_record_by_name(p.user_name, &hr); + r = nss_user_record_by_name(p.user_name, true, &hr); else { _cleanup_(json_variant_unrefp) JsonVariant *last = NULL; @@ -324,9 +325,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) { if (gid_is_valid(p.gid)) - r = nss_group_record_by_gid(p.gid, &g); + r = nss_group_record_by_gid(p.gid, true, &g); else if (p.group_name) - r = nss_group_record_by_name(p.group_name, &g); + r = nss_group_record_by_name(p.group_name, true, &g); else { _cleanup_(json_variant_unrefp) JsonVariant *last = NULL; @@ -467,7 +468,7 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var const char *last = NULL; char **i; - r = nss_group_record_by_name(p.group_name, &g); + r = nss_group_record_by_name(p.group_name, true, &g); if (r == -ESRCH) return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); if (r < 0) @@ -659,7 +660,6 @@ static int process_connection(VarlinkServer *server, int fd) { static int run(int argc, char *argv[]) { usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY; _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL; - _cleanup_close_ int lock = -1; unsigned n_iterations = 0; int m, listen_fd, r; @@ -696,8 +696,8 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m"); listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC; - lock = userdb_nss_compat_disable(); - if (lock < 0) + r = userdb_block_nss_systemd(true); + if (r < 0) return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m"); start_time = now(CLOCK_MONOTONIC); @@ -715,7 +715,8 @@ static int run(int argc, char *argv[]) { n = now(CLOCK_MONOTONIC); if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) { char buf[FORMAT_TIMESPAN_MAX]; - log_debug("Exiting worker, ran for %s, that's enough.", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0)); + log_debug("Exiting worker, ran for %s, that's enough.", + format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0)); break; } @@ -723,7 +724,8 @@ static int run(int argc, char *argv[]) { last_busy_usec = n; else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) { char buf[FORMAT_TIMESPAN_MAX]; - log_debug("Exiting worker, been idle for %s, .", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0)); + log_debug("Exiting worker, been idle for %s.", + format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0)); break; } @@ -736,7 +738,7 @@ static int run(int argc, char *argv[]) { (void) rename_process("systemd-userwork: processing..."); if (fd == -EAGAIN) - continue; /* The listening socket as SO_RECVTIMEO set, hence a time-out is expected + continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected * after a while, let's check if it's time to exit though. */ if (fd == -EINTR) continue; /* Might be that somebody attached via strace, let's just continue in that @@ -745,18 +747,14 @@ static int run(int argc, char *argv[]) { return log_error_errno(fd, "Failed to accept() from listening socket: %m"); if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) { - struct pollfd pfd = { - .fd = listen_fd, - .events = POLLIN, - }; - /* We only slept a very short time? If so, let's see if there are more sockets * pending, and if so, let's ask our parent for more workers */ - if (poll(&pfd, 1, 0) < 0) - return log_error_errno(errno, "Failed to test for POLLIN on listening socket: %m"); + r = fd_wait_for_event(listen_fd, POLLIN, 0); + if (r < 0) + return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m"); - if (FLAGS_SET(pfd.revents, POLLIN)) { + if (FLAGS_SET(r, POLLIN)) { pid_t parent; parent = getppid(); diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 9c2fe9a1b..e475402d9 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -6,9 +6,11 @@ #include "alloc-util.h" #include "crypt-util.h" +#include "fileio.h" #include "hexdecoct.h" #include "log.h" #include "main-func.h" +#include "path-util.h" #include "pretty-print.h" #include "string-util.h" #include "terminal-util.h" @@ -29,7 +31,7 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n" + printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [ROOTHASHSIG]\n" "%s detach VOLUME\n\n" "Attaches or detaches an integrity protected block device.\n" "\nSee the %s for details.\n" @@ -87,7 +89,28 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to configure data device: %m"); - r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY); + if (argc > 6) { +#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY + _cleanup_free_ char *hash_sig = NULL; + size_t hash_sig_size; + char *value; + + if ((value = startswith(argv[6], "base64:"))) { + r = unbase64mem(value, strlen(value), (void *)&hash_sig, &hash_sig_size); + if (r < 0) + return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]); + } else { + r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, &hash_sig, &hash_sig_size); + if (r < 0) + return log_error_errno(r, "Failed to read root hash signature: %m"); + } + + r = crypt_activate_by_signed_key(cd, argv[2], m, l, hash_sig, hash_sig_size, CRYPT_ACTIVATE_READONLY); +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature %s requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()", argv[6]); +#endif + } else + r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY); if (r < 0) return log_error_errno(r, "Failed to set up verity device: %m"); diff --git a/src/version/version.h.in b/src/version/version.h.in index 4219dce83..7b0bf8e26 100644 --- a/src/version/version.h.in +++ b/src/version/version.h.in @@ -1,6 +1,6 @@ /* Detailed project version that includes git commit when not built from a release. * Use this in preference to PROJECT_VERSION, with the following exceptions: - * - where a simplified form is expected for compatiblity, for example + * - where a simplified form is expected for compatibility, for example * 'udevadm version', * - where a simplified machine-parsable form is more useful, for example * pkgconfig files and version information written to binary files. diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c index af78a87d6..e55864d6c 100644 --- a/src/volatile-root/volatile-root.c +++ b/src/volatile-root/volatile-root.c @@ -29,7 +29,7 @@ static int make_volatile(const char *path) { if (r < 0) return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m"); - r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755"); + r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS); if (r < 0) goto finish_rmdir; @@ -80,7 +80,7 @@ static int make_overlay(const char *path) { if (r < 0) return log_error_errno(r, "Couldn't create overlay sysroot directory: %m"); - r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755"); + r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS); if (r < 0) goto finish; diff --git a/src/xdg-autostart-generator/xdg-autostart-condition.c b/src/xdg-autostart-generator/xdg-autostart-condition.c new file mode 100644 index 000000000..84a356165 --- /dev/null +++ b/src/xdg-autostart-generator/xdg-autostart-condition.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "main-func.h" +#include "strv.h" + +/* + * This binary is intended to be run as an ExecCondition= in units generated + * by the xdg-autostart-generator. It does the appropriate checks against + * XDG_CURRENT_DESKTOP that are too advanced for simple ConditionEnvironment= + * matches. + */ + +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), + "Wrong argument count. Expected the OnlyShowIn= and NotShowIn= sets, each colon separated."); + + xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP"); + if (xdg_current_desktop) { + desktops = strv_split(xdg_current_desktop, ":"); + if (!desktops) + return log_oom(); + } + + only_show_in = strv_split(argv[1], ":"); + not_show_in = strv_split(argv[2], ":"); + if (!only_show_in || !not_show_in) + return log_oom(); + + /* Each desktop in XDG_CURRENT_DESKTOP needs to be matched in order. */ + STRV_FOREACH(d, desktops) { + if (strv_contains(only_show_in, *d)) + return 0; + if (strv_contains(not_show_in, *d)) + return 1; + } + + /* non-zero exit code when only_show_in has a proper value */ + return !strv_isempty(only_show_in); +} + +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/xdg-autostart-generator/xdg-autostart-generator.c b/src/xdg-autostart-generator/xdg-autostart-generator.c new file mode 100644 index 000000000..53366a31e --- /dev/null +++ b/src/xdg-autostart-generator/xdg-autostart-generator.c @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include + +#include "dirent-util.h" +#include "fd-util.h" +#include "generator.h" +#include "hashmap.h" +#include "log.h" +#include "main-func.h" +#include "nulstr-util.h" +#include "path-lookup.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "xdg-autostart-service.h" + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free); + +static int enumerate_xdg_autostart(Hashmap *all_services) { + _cleanup_strv_free_ char **autostart_dirs = NULL; + _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"); + if (r < 0) + return r; + r = strv_extend(&autostart_dirs, user_config_autostart_dir); + if (r < 0) + return r; + + r = xdg_user_dirs(&config_dirs, &data_dirs); + if (r < 0) + return r; + r = strv_extend_strv_concat(&autostart_dirs, config_dirs, "/autostart"); + if (r < 0) + return r; + + STRV_FOREACH(path, autostart_dirs) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(*path); + if (!d) { + if (errno != ENOENT) + log_warning_errno(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); + continue; + } + + if (!S_ISREG(st.st_mode)) + continue; + + name = xdg_autostart_service_translate_name(de->d_name); + if (!name) + return log_oom(); + + if (hashmap_contains(all_services, name)) + continue; + + fpath = path_join(*path, de->d_name); + if (!fpath) + return log_oom(); + + service = xdg_autostart_service_parse_desktop(fpath); + if (!service) + return log_oom(); + service->name = TAKE_PTR(name); + + r = hashmap_put(all_services, service->name, service); + if (r < 0) + return log_oom(); + TAKE_PTR(service); + } + } + + return 0; +} + +static int run(const char *dest, const char *dest_early, const char *dest_late) { + _cleanup_(hashmap_freep) Hashmap *all_services = NULL; + XdgAutostartService *service; + Iterator j; + int r; + + assert_se(dest_late); + + all_services = hashmap_new(&xdgautostartservice_hash_ops); + if (!all_services) + return log_oom(); + + r = enumerate_xdg_autostart(all_services); + if (r < 0) + return r; + + HASHMAP_FOREACH(service, all_services, j) + (void) xdg_autostart_service_generate_unit(service, dest_late); + + return 0; +} + +DEFINE_MAIN_GENERATOR_FUNCTION(run); diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c new file mode 100644 index 000000000..4a30f9e43 --- /dev/null +++ b/src/xdg-autostart-generator/xdg-autostart-service.c @@ -0,0 +1,645 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include + +#include "xdg-autostart-service.h" + +#include "conf-parser.h" +#include "escape.h" +#include "unit-name.h" +#include "path-util.h" +#include "fd-util.h" +#include "generator.h" +#include "log.h" +#include "specifier.h" +#include "string-util.h" +#include "nulstr-util.h" +#include "strv.h" + +XdgAutostartService* xdg_autostart_service_free(XdgAutostartService *s) { + if (!s) + return NULL; + + free(s->name); + free(s->path); + free(s->description); + + free(s->type); + free(s->exec_string); + + strv_free(s->only_show_in); + strv_free(s->not_show_in); + + free(s->try_exec); + free(s->autostart_condition); + free(s->kde_autostart_condition); + + free(s->gnome_autostart_phase); + + return mfree(s); +} + +char *xdg_autostart_service_translate_name(const char *name) { + _cleanup_free_ char *c = NULL, *escaped = NULL; + char *res; + + c = strdup(name); + if (!c) + return NULL; + + res = endswith(c, ".desktop"); + if (res) + *res = '\0'; + + escaped = unit_name_escape(c); + if (!escaped) + return NULL; + + return strjoin("app-", escaped, "-autostart.service"); +} + +static int xdg_config_parse_bool( + 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) { + + bool *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "true")) + *b = true; + else if (streq(rvalue, "false")) + *b = false; + else + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid value for boolean: %s", rvalue); + + return 0; +} + +/* Unescapes the string in-place, returns non-zero status on error. */ +static int xdg_unescape_string( + const char *unit, + const char *filename, + int line, + char *str) { + + char *in; + char *out; + + assert(str); + + in = out = str; + + for (; *in; in++, out++) { + if (*in == '\\') { + /* Move forward, and ensure it is a valid escape. */ + in++; + + switch (*in) { + case 's': + *out = ' '; + break; + case 'n': + *out = '\n'; + break; + case 't': + *out = '\t'; + break; + case 'r': + *out = '\r'; + break; + case '\\': + *out = '\\'; + break; + case ';': + /* Technically only permitted for strv. */ + *out = ';'; + break; + default: + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Undefined escape sequence \\%c.", *in); + } + + continue; + } + + *out = *in; + } + *out = '\0'; + + return 0; +} + +/* Note: We do not bother with unescaping the strings, hence the _raw postfix. */ +static int xdg_config_parse_string( + 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 *res = NULL; + char **out = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* XDG does not allow duplicate definitions. */ + if (*out) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Key %s was defined multiple times, ignoring.", lvalue); + return 0; + } + + res = strdup(rvalue); + if (!res) + return log_oom(); + + r = xdg_unescape_string(unit, filename, line, res); + if (r < 0) + return r; + + *out = TAKE_PTR(res); + return 0; +} + +static int strv_strndup_unescape_and_push( + const char *unit, + const char *filename, + unsigned line, + char ***sv, + size_t *n_allocated, + size_t *n, + const char *start, + const char *end) { + + if (end == start) + return 0; + + _cleanup_free_ char *copy = NULL; + int r; + + copy = strndup(start, end - start); + if (!copy) + return log_oom(); + + r = xdg_unescape_string(unit, filename, line, copy); + if (r < 0) + return r; + + if (!greedy_realloc((void**) sv, n_allocated, *n + 2, sizeof(char*))) /* One extra for NULL */ + return log_oom(); + + (*sv)[*n] = TAKE_PTR(copy); + (*sv)[*n + 1] = NULL; + (*n)++; + + return 0; +} + +static int xdg_config_parse_strv( + 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 ***ret_sv = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* XDG does not allow duplicate definitions. */ + if (*ret_sv) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Key %s was already defined, ignoring.", lvalue); + return 0; + } + + size_t n = 0, n_allocated = 0; + _cleanup_strv_free_ char **sv = NULL; + + if (!GREEDY_REALLOC0(sv, n_allocated, 1)) + return log_oom(); + + /* We cannot use strv_split because it does not handle escaping correctly. */ + const char *start = rvalue, *end; + + for (end = start; *end; end++) { + if (*end == '\\') { + /* Move forward, and ensure it is a valid escape. */ + end++; + if (!strchr("sntr\\;", *end)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Undefined escape sequence \\%c.", *end); + return 0; + } + continue; + } + + if (*end == ';') { + r = strv_strndup_unescape_and_push(unit, filename, line, + &sv, &n_allocated, &n, + start, end); + if (r < 0) + return r; + + start = end + 1; + } + } + + /* Handle the trailing entry after the last separator */ + r = strv_strndup_unescape_and_push(unit, filename, line, + &sv, &n_allocated, &n, + start, end); + if (r < 0) + return r; + + *ret_sv = TAKE_PTR(sv); + return 0; +} + +static int xdg_config_item_table_lookup( + const void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + assert(lvalue); + + /* Ignore any keys with [] as those are translations. */ + if (strchr(lvalue, '[')) { + *func = NULL; + *ltype = 0; + *data = NULL; + return 1; + } + + return config_item_table_lookup(table, section, lvalue, func, ltype, data, userdata); +} + +XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path) { + _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL; + int r; + + service = new0(XdgAutostartService, 1); + if (!service) + return NULL; + + service->path = strdup(path); + if (!service->path) + return NULL; + + const ConfigTableItem items[] = { + { "Desktop Entry", "Name", xdg_config_parse_string, 0, &service->description}, + { "Desktop Entry", "Exec", xdg_config_parse_string, 0, &service->exec_string}, + { "Desktop Entry", "TryExec", xdg_config_parse_string, 0, &service->try_exec}, + { "Desktop Entry", "Type", xdg_config_parse_string, 0, &service->type}, + { "Desktop Entry", "OnlyShowIn", xdg_config_parse_strv, 0, &service->only_show_in}, + { "Desktop Entry", "NotShowIn", xdg_config_parse_strv, 0, &service->not_show_in}, + { "Desktop Entry", "Hidden", xdg_config_parse_bool, 0, &service->hidden}, + { "Desktop Entry", "AutostartCondition", xdg_config_parse_string, 0, &service->autostart_condition}, + { "Desktop Entry", "X-KDE-autostart-condition", xdg_config_parse_string, 0, &service->kde_autostart_condition}, + { "Desktop Entry", "X-GNOME-Autostart-Phase", xdg_config_parse_string, 0, &service->gnome_autostart_phase}, + { "Desktop Entry", "X-systemd-skip", xdg_config_parse_bool, 0, &service->systemd_skip}, + + /* Common entries that we do not use currently. */ + { "Desktop Entry", "Categories", NULL, 0, NULL}, + { "Desktop Entry", "Comment", NULL, 0, NULL}, + { "Desktop Entry", "Encoding", NULL, 0, NULL}, + { "Desktop Entry", "GenericName", NULL, 0, NULL}, + { "Desktop Entry", "Icon", NULL, 0, NULL}, + { "Desktop Entry", "Keywords", NULL, 0, NULL}, + { "Desktop Entry", "NoDisplay", NULL, 0, NULL}, + { "Desktop Entry", "StartupNotify", NULL, 0, NULL}, + { "Desktop Entry", "Terminal", NULL, 0, NULL}, + { "Desktop Entry", "Version", NULL, 0, NULL}, + {} + }; + + r = config_parse(NULL, service->path, NULL, + "Desktop Entry\0", + xdg_config_item_table_lookup, items, + CONFIG_PARSE_WARN, service, + NULL); + /* If parsing failed, only hide the file so it will still mask others. */ + if (r < 0) { + log_warning_errno(r, "Failed to parse %s, ignoring it", service->path); + service->hidden = true; + } + + return TAKE_PTR(service); +} + +int xdg_autostart_format_exec_start( + const char *exec, + char **ret_exec_start) { + + _cleanup_strv_free_ char **exec_split = NULL; + char *res; + size_t n, i; + bool first_arg; + int r; + + /* + * Unfortunately, there is a mismatch between systemd's idea of $PATH + * and XDGs. i.e. we need to ensure that we have an absolute path to + * support cases where $PATH has been modified from the default set. + * + * Note that this is only needed for development environments though; + * so while it is important, this should have no effect in production + * environments. + * + * To be compliant with the XDG specification, we also need to strip + * certain parameters and such. Doing so properly makes parsing the + * command line unavoidable. + * + * NOTE: Technically, XDG only specifies " as quotes, while this also + * accepts '. + */ + exec_split = strv_split_full(exec, WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX); + if (!exec_split) + return -ENOMEM; + + if (strv_isempty(exec_split)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty"); + + first_arg = true; + for (i = n = 0; exec_split[i]; i++) { + _cleanup_free_ char *c = NULL, *raw = NULL, *p = NULL, *escaped = NULL, *quoted = NULL; + + r = cunescape(exec_split[i], 0, &c); + if (r < 0) + return log_debug_errno(r, "Failed to unescape '%s': %m", exec_split[i]); + + if (first_arg) { + _cleanup_free_ char *executable = NULL; + + /* This is the executable, find it in $PATH */ + first_arg = false; + r = find_binary(c, &executable); + 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(exec_split[n]); + exec_split[n++] = TAKE_PTR(escaped); + continue; + } + + /* + * Remove any standardised XDG fields; we assume they never appear as + * part of another argument as that just does not make any sense as + * they can be empty (GLib will e.g. turn "%f" into an empty argument). + * Other implementations may handle this differently. + */ + if (STR_IN_SET(c, + "%f", "%F", + "%u", "%U", + "%d", "%D", + "%n", "%N", + "%i", /* Location of icon, could be implemented. */ + "%c", /* Translated application name, could be implemented. */ + "%k", /* Location of desktop file, could be implemented. */ + "%v", + "%m" + )) + continue; + + /* + * %% -> % and then % -> %% means that we correctly quote any % + * and also quote any left over (and invalid) % specifier from + * the desktop file. + */ + raw = strreplace(c, "%%", "%"); + if (!raw) + return log_oom(); + p = strreplace(raw, "%", "%%"); + if (!p) + return log_oom(); + escaped = cescape(p); + if (!escaped) + return log_oom(); + + quoted = strjoin("\"", escaped, "\""); + if (!quoted) + return log_oom(); + + free(exec_split[n]); + exec_split[n++] = TAKE_PTR(quoted); + } + for (; exec_split[n]; n++) + exec_split[n] = mfree(exec_split[n]); + + res = strv_join(exec_split, " "); + if (!res) + return log_oom(); + + *ret_exec_start = res; + return 0; +} + +static int xdg_autostart_generate_desktop_condition( + FILE *f, + const char *test_binary, + const char *condition) { + + int r; + + /* Generate an ExecCondition for GNOME autostart condition */ + if (!isempty(condition)) { + _cleanup_free_ char *gnome_autostart_condition_path = NULL, *e_autostart_condition = NULL; + + r = find_binary(test_binary, &gnome_autostart_condition_path); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r, + "%s not found: %m", test_binary); + fprintf(f, "# ExecCondition using %s skipped due to missing binary.\n", test_binary); + return r; + } + + e_autostart_condition = cescape(condition); + if (!e_autostart_condition) + return log_oom(); + + fprintf(f, + "ExecCondition=%s --condition \"%s\"\n", + gnome_autostart_condition_path, + e_autostart_condition); + } + + return 0; +} + +int xdg_autostart_service_generate_unit( + XdgAutostartService *service, + const char *dest) { + + _cleanup_free_ char *path_escaped = NULL, *exec_start = NULL, *unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(service); + + /* Nothing to do for hidden services. */ + if (service->hidden) { + log_info("Not generating service for XDG autostart %s, it is hidden.", service->name); + return 0; + } + + if (service->systemd_skip) { + log_info("Not generating service for XDG autostart %s, should be skipped by generator.", service->name); + return 0; + } + + /* Nothing to do if type is not Application. */ + if (!streq_ptr(service->type, "Application")) { + log_info("Not generating service for XDG autostart %s, only Type=Application is supported.", service->name); + return 0; + } + + if (!service->exec_string) { + log_warning("Not generating service for XDG autostart %s, it is has no Exec= line.", service->name); + return 0; + } + + /* + * The TryExec key cannot be checked properly from the systemd unit, + * it is trivial to check using find_binary though. + */ + if (service->try_exec) { + r = find_binary(service->try_exec, NULL); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r, + "Not generating service for XDG autostart %s, could not find TryExec= binary %s: %m", + service->name, 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); + return 0; + } + + if (service->gnome_autostart_phase) { + /* There is no explicit value for the "Application" phase. */ + log_info("Not generating service for XDG autostart %s, startup phases are not supported.", + service->name); + return 0; + } + + path_escaped = specifier_escape(service->path); + if (!path_escaped) + return log_oom(); + + unit = path_join(dest, service->name); + if (!unit) + return log_oom(); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-xdg-autostart-generator\n\n" + "[Unit]\n" + "Documentation=man:systemd-xdg-autostart-generator(8)\n" + "SourcePath=%s\n" + "PartOf=graphical-session.target\n\n", + path_escaped); + + if (service->description) { + _cleanup_free_ char *t = NULL; + + t = specifier_escape(service->description); + if (!t) + return log_oom(); + + fprintf(f, "Description=%s\n", t); + } + + /* Only start after the session is ready. */ + fprintf(f, + "After=graphical-session.target\n"); + + fprintf(f, + "\n[Service]\n" + "Type=simple\n" + "ExecStart=:%s\n" + "Restart=no\n" + "TimeoutSec=5s\n" + "Slice=app.slice\n", + exec_start); + + /* Generate an ExecCondition to check $XDG_CURRENT_DESKTOP */ + if (!strv_isempty(service->only_show_in) || !strv_isempty(service->not_show_in)) { + _cleanup_free_ char *only_show_in = NULL, *not_show_in = NULL, *e_only_show_in = NULL, *e_not_show_in = NULL; + + only_show_in = strv_join(service->only_show_in, ":"); + not_show_in = strv_join(service->not_show_in, ":"); + if (!only_show_in || !not_show_in) + return log_oom(); + + e_only_show_in = cescape(only_show_in); + e_not_show_in = cescape(not_show_in); + if (!e_only_show_in || !e_not_show_in) + return log_oom(); + + /* Just assume the values are reasonably sane */ + fprintf(f, + "ExecCondition=" ROOTLIBEXECDIR "/systemd-xdg-autostart-condition \"%s\" \"%s\"\n", + e_only_show_in, + e_not_show_in); + } + + r = xdg_autostart_generate_desktop_condition(f, + "gnome-systemd-autostart-condition", + service->autostart_condition); + if (r < 0) + return r; + + r = xdg_autostart_generate_desktop_condition(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; +} diff --git a/src/xdg-autostart-generator/xdg-autostart-service.h b/src/xdg-autostart-generator/xdg-autostart-service.h new file mode 100644 index 000000000..685f97824 --- /dev/null +++ b/src/xdg-autostart-generator/xdg-autostart-service.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "macro.h" + +typedef struct XdgAutostartService { + char *name; + char *path; + char *description; /* Name in XDG desktop file */ + + char *type; /* Purely as an assertion check */ + char *exec_string; + + char **only_show_in; + char **not_show_in; + + char *try_exec; + char *autostart_condition; /* This is mostly GNOME specific */ + char *kde_autostart_condition; + + char *gnome_autostart_phase; + + bool hidden; + bool systemd_skip; + +} XdgAutostartService; + + +XdgAutostartService * xdg_autostart_service_free(XdgAutostartService *s); +DEFINE_TRIVIAL_CLEANUP_FUNC(XdgAutostartService*, xdg_autostart_service_free); + +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); diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in index 47bf84769..da76fd71d 100644 --- a/sysctl.d/50-coredump.conf.in +++ b/sysctl.d/50-coredump.conf.in @@ -5,8 +5,23 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. -# See sysctl.d(5) for the description of the files in this directory, -# and systemd-coredump(8) and core(5) for the explanation of the -# setting below. +# See sysctl.d(5) for the description of the files in this directory. +# Pipe the core file to systemd-coredump. The systemd-coredump process spawned +# by the kernel will start a second copy of itself as the +# systemd-coredump@.service, which will do the actual processing and storing of +# the core dump. +# +# See systemd-coredump(8) and core(5). kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h + +# Also dump processes executing a set-user-ID/set-group-ID program that is +# owned by a user/group other than the real user/group ID of the process, or +# a program that has file capabilities. ("2" is called "suidsafe" in core(5)). +# +# systemd-coredump will store the core file owned by the effective uid and gid +# of the running process (and not the filesystem-user-ID which the kernel uses +# when saving a core dump). +# +# See proc(5), setuid(2), capabilities(7). +fs.suid_dumpable=2 diff --git a/test/README.testsuite b/test/README.testsuite index 471771acd..7204fdb00 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -10,10 +10,10 @@ ninja: no work to do. --x-- Running TEST-01-BASIC --x-- + make -C TEST-01-BASIC BUILD_DIR=/home/zbyszek/src/systemd/build clean setup run make: Entering directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC' -TEST CLEANUP: Basic systemd setup -TEST SETUP: Basic systemd setup +TEST-01-BASIC CLEANUP: Basic systemd setup +TEST-01-BASIC SETUP: Basic systemd setup ... -TEST RUN: Basic systemd setup [OK] +TEST-01-BASIC RUN: Basic systemd setup [OK] make: Leaving directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC' --x-- Result of TEST-01-BASIC: 0 --x-- --x-- Running TEST-02-CRYPTSETUP --x-- diff --git a/test/TEST-01-BASIC/Makefile b/test/TEST-01-BASIC/Makefile index 45e9bfc67..79fe9688b 100644 --- a/test/TEST-01-BASIC/Makefile +++ b/test/TEST-01-BASIC/Makefile @@ -1,9 +1,6 @@ BUILD_DIR=$(shell ../../tools/find-build-dir.sh) -all setup run: +all setup run clean clean-again: @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ -clean clean-again: - @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --clean - .PHONY: all setup run clean clean-again diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh index 0eaa8f991..58f6cd141 100755 --- a/test/TEST-01-BASIC/test.sh +++ b/test/TEST-01-BASIC/test.sh @@ -1,34 +1,25 @@ #!/usr/bin/env bash set -e TEST_DESCRIPTION="Basic systemd setup" +IMAGE_NAME="basic" RUN_IN_UNPRIVILEGED_CONTAINER=${RUN_IN_UNPRIVILEGED_CONTAINER:-yes} +TEST_REQUIRE_INSTALL_TESTS=0 . $TEST_BASE_DIR/test-functions -test_setup() { +test_create_image() { create_empty_image_rootdir # Create what will eventually be our root filesystem onto an overlay ( LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - setup_basic_environment - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service < /failed ; systemctl daemon-reload ; echo OK > /testok' -Type=oneshot -EOF - - setup_testsuite + # install tests manually so the test is functional even when -Dinstall-tests=false + mkdir -p $initdir/usr/lib/systemd/tests/testdata/units/ + cp -v $(dirname $0)/../units/{testsuite-01,end}.service $initdir/usr/lib/systemd/tests/testdata/units/ ) setup_nspawn_root } -do_test "$@" +do_test "$@" 01 diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh index a859b345d..ef1811122 100755 --- a/test/TEST-02-CRYPTSETUP/test.sh +++ b/test/TEST-02-CRYPTSETUP/test.sh @@ -1,30 +1,29 @@ #!/usr/bin/env bash set -e TEST_DESCRIPTION="cryptsetup systemd setup" +IMAGE_NAME="cryptsetup" TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions check_result_qemu() { ret=1 - mkdir -p $initdir - mount ${LOOPDEV}p1 $initdir + mount_initdir [[ -e $initdir/testok ]] && ret=0 [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile mount /dev/mapper/varcrypt $initdir/var - cp -a $initdir/var/log/journal $TESTDIR - umount $initdir/var - umount $initdir + save_journal $initdir/var/log/journal + _umount_dir $initdir/var + _umount_dir $initdir cryptsetup luksClose /dev/mapper/varcrypt [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed - ls -l $TESTDIR/journal/*/*.journal + echo $JOURNAL_LIST test -s $TESTDIR/failed && ret=$(($ret+1)) return $ret } - -test_setup() { +test_create_image() { create_empty_image_rootdir echo -n test >$TESTDIR/keyfile cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 ${LOOPDEV}p2 $TESTDIR/keyfile @@ -42,30 +41,21 @@ test_setup() { setup_basic_environment mask_supporting_services - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service < /failed ; echo OK > /testok' -Type=oneshot -EOF - - setup_testsuite - install_dmevent generate_module_dependencies cat >$initdir/etc/crypttab < $initdir/etc/varkey + echo -n test >$initdir/etc/varkey cat $initdir/etc/crypttab | ddebug cat >>$initdir/etc/fstab <> $initdir/etc/systemd/journald.conf ) } @@ -82,8 +72,8 @@ test_cleanup() { } test_setup_cleanup() { - cleanup_root_var - _test_setup_cleanup + cleanup_root_var || : + cleanup_initdir } -do_test "$@" +do_test "$@" 02 diff --git a/test/TEST-03-JOBS/test.sh b/test/TEST-03-JOBS/test.sh index 5299464b8..221a18682 100755 --- a/test/TEST-03-JOBS/test.sh +++ b/test/TEST-03-JOBS/test.sh @@ -2,39 +2,8 @@ set -e TEST_DESCRIPTION="Job-related tests" TEST_NO_QEMU=1 +IMAGE_NAME="default" . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image_rootdir - - # Create what will eventually be our root filesystem onto an overlay - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/forever-print-hola.service <$initdir/etc/systemd/system.conf <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service -[Unit] -Description=Testsuite service - -[Service] -ExecStart=/test-selinux-checks.sh -Type=oneshot -EOF - - cat <$initdir/etc/systemd/system/hola.service -[Service] -Type=oneshot -ExecStart=/bin/echo Start Hola -ExecReload=/bin/echo Reload Hola -ExecStop=/bin/echo Stop Hola -RemainAfterExit=yes -EOF - - setup_testsuite - - cat <$initdir/etc/systemd/system/load-systemd-test-module.service -[Unit] -Description=Load systemd-test module -DefaultDependencies=no -Requires=local-fs.target -Conflicts=shutdown.target -After=local-fs.target -Before=sysinit.target shutdown.target autorelabel.service -ConditionSecurity=selinux -ConditionPathExists=|/.load-systemd-test-module - -[Service] -ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && cd /systemd-test-module && make -f /usr/share/selinux/devel/Makefile load && rm /.load-systemd-test-module' -Type=oneshot -TimeoutSec=0 -RemainAfterExit=yes -EOF - - touch $initdir/.load-systemd-test-module - mkdir -p $initdir/etc/systemd/system/basic.target.wants - ln -fs load-systemd-test-module.service $initdir/etc/systemd/system/basic.target.wants/load-systemd-test-module.service - local _modules_dir=/var/lib/selinux rm -rf $initdir/$_modules_dir if ! cp -ar $_modules_dir $initdir/$_modules_dir; then @@ -87,11 +44,12 @@ EOF mkdir $initdir/systemd-test-module cp systemd_test.te $initdir/systemd-test-module cp systemd_test.if $initdir/systemd-test-module - cp test-selinux-checks.sh $initdir dracut_install -o sesearch dracut_install runcon - dracut_install checkmodule semodule semodule_package m4 make /usr/libexec/selinux/hll/pp load_policy sefcontext_compile + dracut_install checkmodule semodule semodule_package m4 make load_policy sefcontext_compile + dracut_install -o /usr/libexec/selinux/hll/pp # Fedora/RHEL/... + dracut_install -o /usr/lib/selinux/hll/pp # Debian/Ubuntu/... ) } -do_test "$@" +do_test "$@" 06 diff --git a/test/TEST-07-ISSUE-1981/test.sh b/test/TEST-07-ISSUE-1981/test.sh index 7927294a8..5da24a987 100755 --- a/test/TEST-07-ISSUE-1981/test.sh +++ b/test/TEST-07-ISSUE-1981/test.sh @@ -7,32 +7,4 @@ TEST_NO_QEMU=1 NSPAWN_TIMEOUT=30 -test_setup() { - create_empty_image_rootdir - - # Create what will eventually be our root filesystem onto an overlay - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < /testok; systemctl poweroff' -Type=oneshot -EOF - - rm $initdir/etc/fstab - cat >$initdir/etc/systemd/system/-.mount <$initdir/etc/systemd/system/systemd-remount-fs.service <$initdir/etc/systemd/system/testsuite.service <<'EOF' -[Unit] -Description=Testsuite service - -[Service] -Type=oneshot -ExecStart=/bin/sh -c '>/testok' -RemainAfterExit=yes -ExecStop=/bin/sh -c 'kill -SEGV $$$$' -TimeoutStopSec=270s -EOF - - setup_testsuite - ) -} - -do_test "$@" +do_test "$@" 09 diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh index 1761ad1e4..14ded56ba 100755 --- a/test/TEST-10-ISSUE-2467/test.sh +++ b/test/TEST-10-ISSUE-2467/test.sh @@ -4,45 +4,4 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467" . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image_rootdir - - # Create what will eventually be our root filesystem onto an overlay - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - dracut_install true rm socat - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <<'EOF' -[Unit] -Description=Testsuite service - -[Service] -Type=oneshot -ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; printf x > test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok' -EOF - - cat >$initdir/etc/systemd/system/test.socket <<'EOF' -[Socket] -ListenStream=/run/test.ctl -EOF - - cat > $initdir/etc/systemd/system/test.service <<'EOF' -[Unit] -Requires=test.socket -ConditionPathExistsGlob=/tmp/nonexistent - -[Service] -ExecStart=/bin/true -EOF - - setup_testsuite - ) - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 10 diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh index e444414a9..da003c90d 100755 --- a/test/TEST-11-ISSUE-3166/test.sh +++ b/test/TEST-11-ISSUE-3166/test.sh @@ -5,58 +5,4 @@ TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image_rootdir - - # Create what will eventually be our root filesystem onto an overlay - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - dracut_install false touch - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/fail-on-restart.service <$initdir/test-fail-on-restart.sh <<'EOF' -#!/usr/bin/env bash -set -x - -systemctl start fail-on-restart.service -active_state=$(systemctl show --property ActiveState fail-on-restart.service) -while [[ "$active_state" == "ActiveState=activating" || "$active_state" == "ActiveState=active" ]]; do - sleep 1 - active_state=$(systemctl show --property ActiveState fail-on-restart.service) -done -systemctl is-failed fail-on-restart.service || exit 1 -touch /testok -EOF - - chmod 0755 $initdir/test-fail-on-restart.sh - setup_testsuite - ) -} - -do_test "$@" +do_test "$@" 11 diff --git a/test/TEST-12-ISSUE-3171/test.sh b/test/TEST-12-ISSUE-3171/test.sh index e30c36ed8..c8abefbd8 100755 --- a/test/TEST-12-ISSUE-3171/test.sh +++ b/test/TEST-12-ISSUE-3171/test.sh @@ -5,85 +5,4 @@ TEST_NO_QEMU=1 . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image_rootdir - - # Create what will eventually be our root filesystem onto an overlay - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - dracut_install cat mv stat nc - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/test-socket-group.sh <<'EOF' -#!/usr/bin/env bash -set -x -set -e -set -o pipefail - -U=/run/systemd/system/test.socket -cat <<'EOL' >$U -[Unit] -Description=Test socket -[Socket] -Accept=yes -ListenStream=/run/test.socket -SocketGroup=adm -SocketMode=0660 -EOL - -cat <<'EOL' > /run/systemd/system/test@.service -[Unit] -Description=Test service -[Service] -StandardInput=socket -ExecStart=/bin/sh -x -c cat -EOL - -systemctl start test.socket -systemctl is-active test.socket -[[ "$(stat --format='%G' /run/test.socket)" == adm ]] -echo A | nc -w1 -U /run/test.socket - -mv $U ${U}.disabled -systemctl daemon-reload -systemctl is-active test.socket -[[ "$(stat --format='%G' /run/test.socket)" == adm ]] -echo B | nc -w1 -U /run/test.socket && exit 1 - -mv ${U}.disabled $U -systemctl daemon-reload -systemctl is-active test.socket -echo C | nc -w1 -U /run/test.socket && exit 1 -[[ "$(stat --format='%G' /run/test.socket)" == adm ]] - -systemctl restart test.socket -systemctl is-active test.socket -echo D | nc -w1 -U /run/test.socket -[[ "$(stat --format='%G' /run/test.socket)" == adm ]] - - -touch /testok -EOF - - chmod 0755 $initdir/test-socket-group.sh - setup_testsuite - ) - - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 12 diff --git a/test/TEST-13-NSPAWN-SMOKE/Makefile b/test/TEST-13-NSPAWN-SMOKE/Makefile deleted file mode 100644 index e5e335021..000000000 --- a/test/TEST-13-NSPAWN-SMOKE/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -BUILD_DIR=$(shell ../../tools/find-build-dir.sh) - -all setup run: - @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ - -clean clean-again: - @basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean - @rm -f has-overflow - -.PHONY: all setup run clean clean-again diff --git a/test/TEST-13-NSPAWN-SMOKE/Makefile b/test/TEST-13-NSPAWN-SMOKE/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-13-NSPAWN-SMOKE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh index 974b239d8..236378a38 100755 --- a/test/TEST-13-NSPAWN-SMOKE/test.sh +++ b/test/TEST-13-NSPAWN-SMOKE/test.sh @@ -1,193 +1,23 @@ #!/usr/bin/env bash set -e TEST_DESCRIPTION="systemd-nspawn smoke test" +IMAGE_NAME="nspawn" TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions -test_setup() { +test_create_image() { create_empty_image_rootdir # Create what will eventually be our root filesystem onto an overlay ( LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - setup_basic_environment mask_supporting_services - dracut_install busybox chmod rmdir unshare ip sysctl - cp create-busybox-container $initdir/ - - ./create-busybox-container $initdir/nc-container - initdir="$initdir/nc-container" dracut_install nc ip - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/test-nspawn.sh <<'EOF' -#!/usr/bin/env bash -set -x -set -e -set -u -set -o pipefail - -export SYSTEMD_LOG_LEVEL=debug - -# check cgroup-v2 -is_v2_supported=no -mkdir -p /tmp/cgroup2 -if mount -t cgroup2 cgroup2 /tmp/cgroup2; then - is_v2_supported=yes - umount /tmp/cgroup2 -fi -rmdir /tmp/cgroup2 - -# check cgroup namespaces -is_cgns_supported=no -if [[ -f /proc/1/ns/cgroup ]]; then - is_cgns_supported=yes -fi - -is_user_ns_supported=no -# On some systems (e.g. CentOS 7) the default limit for user namespaces -# is set to 0, which causes the following unshare syscall to fail, even -# with enabled user namespaces support. By setting this value explicitly -# we can ensure the user namespaces support to be detected correctly. -sysctl -w user.max_user_namespaces=10000 -if unshare -U sh -c :; then - is_user_ns_supported=yes -fi - -function check_bind_tmp_path { - # https://github.com/systemd/systemd/issues/4789 - local _root="/var/lib/machines/bind-tmp-path" - /create-busybox-container "$_root" - >/tmp/bind - systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind' -} - -function check_norbind { - # https://github.com/systemd/systemd/issues/13170 - local _root="/var/lib/machines/norbind-path" - mkdir -p /tmp/binddir/subdir - echo -n "outer" > /tmp/binddir/subdir/file - mount -t tmpfs tmpfs /tmp/binddir/subdir - echo -n "inner" > /tmp/binddir/subdir/file - /create-busybox-container "$_root" - systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi' -} - -function check_notification_socket { - # https://github.com/systemd/systemd/issues/4944 - local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/notify' - systemd-nspawn --register=no -D /nc-container /bin/sh -x -c "$_cmd" - systemd-nspawn --register=no -D /nc-container -U /bin/sh -x -c "$_cmd" -} - -function run { - if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then - printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2 - return 0 - fi - if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]]; then - printf "CGroup namespaces are not supported. Skipping.\n" >&2 - return 0 - fi - - local _root="/var/lib/machines/unified-$1-cgns-$2-api-vfs-writable-$3" - /create-busybox-container "$_root" - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then - [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1 - else - [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then - [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1 - else - [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1 - fi - - local _netns_opt="--network-namespace-path=/proc/self/ns/net" - - # --network-namespace-path and network-related options cannot be used together - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then - return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then - return 1 - fi - - # allow combination of --network-namespace-path and --private-network - if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then - return 1 - fi - - # test --network-namespace-path works with a network namespace created by "ip netns" - ip netns add nspawn_test - _netns_opt="--network-namespace-path=/run/netns/nspawn_test" - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP' - local r=$? - ip netns del nspawn_test - - if [ $r -ne 0 ]; then - return 1 - fi - - return 0 -} - -check_bind_tmp_path - -check_norbind - -check_notification_socket - -for api_vfs_writable in yes no network; do - run no no $api_vfs_writable - run yes no $api_vfs_writable - run no yes $api_vfs_writable - run yes yes $api_vfs_writable -done - -touch /testok -EOF - - chmod 0755 $initdir/test-nspawn.sh - setup_testsuite + ../create-busybox-container $initdir/testsuite-13.nc-container + initdir="$initdir/testsuite-13.nc-container" dracut_install nc ip md5sum ) } -do_test "$@" +do_test "$@" 13 diff --git a/test/TEST-14-MACHINE-ID/test.sh b/test/TEST-14-MACHINE-ID/test.sh index 74cabf86a..e8dbf23c5 100755 --- a/test/TEST-14-MACHINE-ID/test.sh +++ b/test/TEST-14-MACHINE-ID/test.sh @@ -1,78 +1,21 @@ #!/usr/bin/env bash set -e TEST_DESCRIPTION="/etc/machine-id testing" +IMAGE_NAME="badid" TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions -test_setup() { +test_create_image() { create_empty_image_rootdir # Create what will eventually be our root filesystem onto an overlay ( LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - setup_basic_environment mask_supporting_services printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >$initdir/etc/machine-id - dracut_install mount cmp - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service < /failed ; echo OK > /testok' -Type=oneshot -EOF - -cat >$initdir/test-machine-id-setup.sh <<'EOF' -#!/usr/bin/env bash - -set -e -set -x - -function setup_root { - local _root="$1" - mkdir -p "$_root" - mount -t tmpfs tmpfs "$_root" - mkdir -p "$_root/etc" "$_root/run" -} - -function check { - printf "Expected\n" - cat "$1" - printf "\nGot\n" - cat "$2" - cmp "$1" "$2" -} - -r="$(pwd)/overwrite-broken-machine-id" -setup_root "$r" -systemd-machine-id-setup --print --root "$r" -echo abc >>"$r/etc/machine-id" -id=$(systemd-machine-id-setup --print --root "$r") -echo $id >expected -check expected "$r/etc/machine-id" - -r="$(pwd)/transient-machine-id" -setup_root "$r" -systemd-machine-id-setup --print --root "$r" -echo abc >>"$r/etc/machine-id" -mount -o remount,ro "$r" -mount -t tmpfs tmpfs "$r/run" -transient_id=$(systemd-machine-id-setup --print --root "$r") -mount -o remount,rw "$r" -commited_id=$(systemd-machine-id-setup --print --commit --root "$r") -[[ "$transient_id" = "$commited_id" ]] -check "$r/etc/machine-id" "$r/run/machine-id" -EOF -chmod +x $initdir/test-machine-id-setup.sh - - setup_testsuite ) } -do_test "$@" +do_test "$@" 14 diff --git a/test/TEST-15-DROPIN/test.sh b/test/TEST-15-DROPIN/test.sh index 63bbd3505..1540e2e1f 100755 --- a/test/TEST-15-DROPIN/test.sh +++ b/test/TEST-15-DROPIN/test.sh @@ -5,18 +5,4 @@ TEST_NO_QEMU=1 . $TEST_BASE_DIR/test-functions -test_setup() { - # create the basic filesystem layout - setup_basic_environment - mask_supporting_services - - # import the test scripts in the rootfs and plug them in systemd - cp testsuite.service $initdir/etc/systemd/system/ - cp test-dropin.sh $initdir/ - setup_testsuite - - # create dedicated rootfs for nspawn (located in $TESTDIR/nspawn-root) - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 15 diff --git a/test/TEST-16-EXTEND-TIMEOUT/test.sh b/test/TEST-16-EXTEND-TIMEOUT/test.sh index 43d9f1278..e1e2a68fa 100755 --- a/test/TEST-16-EXTEND-TIMEOUT/test.sh +++ b/test/TEST-16-EXTEND-TIMEOUT/test.sh @@ -6,30 +6,4 @@ TEST_NO_QEMU=1 . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image - - # Create what will eventually be our root filesystem onto an overlay - ( - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - for s in success-all success-start success-stop success-runtime \ - fail-start fail-stop fail-runtime - do - cp testsuite-${s}.service ${initdir}/etc/systemd/system - done - cp testsuite.service ${initdir}/etc/systemd/system - - cp extend_timeout_test_service.sh ${initdir}/ - cp assess.sh ${initdir}/ - - setup_testsuite - ) - - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 16 diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite.service deleted file mode 100644 index 7512ba9e1..000000000 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite.service +++ /dev/null @@ -1,18 +0,0 @@ - -[Unit] -Description=Testsuite: Assess all other testsuite-*.services worked as expected - -Wants=testsuite-success-all.service -Wants=testsuite-success-start.service -Wants=testsuite-success-runtime.service -Wants=testsuite-success-stop.service -Wants=testsuite-fail-start.service -Wants=testsuite-fail-stop.service -Wants=testsuite-fail-runtime.service -StopWhenUnneeded=yes - -[Service] -Type=simple -TimeoutStartSec=infinity -ExecStartPre=/assess.sh -ExecStart=/bin/true diff --git a/test/TEST-17-UDEV-WANTS/test.sh b/test/TEST-17-UDEV-WANTS/test.sh index e196003e8..5b8f22cba 100755 --- a/test/TEST-17-UDEV-WANTS/test.sh +++ b/test/TEST-17-UDEV-WANTS/test.sh @@ -6,29 +6,4 @@ TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions QEMU_TIMEOUT=300 -test_setup() { - create_empty_image_rootdir - - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < /dev/null - journalctl --sync - journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err + systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then echo "**** Unexpected error output for $f" cat $TESTDIR/tmp/err diff --git a/test/TEST-22-TMPFILES/test.sh b/test/TEST-22-TMPFILES/test.sh index aa6efcfb4..317e4a88f 100755 --- a/test/TEST-22-TMPFILES/test.sh +++ b/test/TEST-22-TMPFILES/test.sh @@ -2,30 +2,6 @@ set -e TEST_DESCRIPTION="Tmpfiles related tests" TEST_NO_QEMU=1 - . $TEST_BASE_DIR/test-functions -test_setup() { - # create the basic filesystem layout - setup_basic_environment - mask_supporting_services - inst_binary mv - inst_binary stat - inst_binary seq - inst_binary xargs - inst_binary mkfifo - inst_binary readlink - - # setup the testsuite service - cp testsuite.service $initdir/etc/systemd/system/ - setup_testsuite - - mkdir -p $initdir/testsuite - cp run-tmpfiles-tests.sh $initdir/testsuite/ - cp test-*.sh $initdir/testsuite/ - - # create dedicated rootfs for nspawn (located in $TESTDIR/nspawn-root) - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 22 diff --git a/test/TEST-23-TYPE-EXEC/test.sh b/test/TEST-23-TYPE-EXEC/test.sh index ebc9fe4c8..1b0d25a72 100755 --- a/test/TEST-23-TYPE-EXEC/test.sh +++ b/test/TEST-23-TYPE-EXEC/test.sh @@ -1,33 +1,6 @@ #!/usr/bin/env bash set -e TEST_DESCRIPTION="test Type=exec" - . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image_rootdir - - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/specifier-j-wants.service << EOF -[Unit] -Description=Wants with percent-j specifier -Wants=specifier-j-depends-%j.service -After=specifier-j-depends-%j.service - -[Service] -Type=oneshot -ExecStart=test -f /tmp/test-specifier-j-%j -ExecStart=/bin/sh -c 'echo OK > /testok' -EOF - cat >$initdir/etc/systemd/system/specifier-j-depends-wants.service << EOF -[Unit] -Description=Dependent service for percent-j specifier - -[Service] -Type=oneshot -ExecStart=touch /tmp/test-specifier-j-wants -EOF - cat >$initdir/etc/systemd/system/testsuite.service << EOF -[Unit] -Description=Testsuite: Ensure %j Wants directives work -Wants=specifier-j-wants.service -After=specifier-j-wants.service - -[Service] -Type=oneshot -ExecStart=/bin/true -EOF - - setup_testsuite - ) - - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 28 diff --git a/test/TEST-29-UDEV-ID_RENAMING/test.sh b/test/TEST-29-UDEV-ID_RENAMING/test.sh index fb570b034..4feafc04d 100755 --- a/test/TEST-29-UDEV-ID_RENAMING/test.sh +++ b/test/TEST-29-UDEV-ID_RENAMING/test.sh @@ -6,29 +6,4 @@ TEST_NO_NSPAWN=1 . $TEST_BASE_DIR/test-functions QEMU_TIMEOUT=300 -test_setup() { - create_empty_image_rootdir - - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/systemd-timedated.service.d/watchdog.conf <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf < - - - - - - -EOF - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/sysusers.d/testuser.conf <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < /testok - exit 0 -fi - -systemd-analyze log-level debug - -truncate -s 1G /tmp/zzz - -SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8 - -systemd-repart /tmp/zzz --empty=force --dry-run=no --seed=$SEED - -sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/empty - -cmp /tmp/empty - < /tmp/definitions/root.conf < /tmp/definitions/home.conf < /tmp/definitions/swap.conf < /tmp/populated - -cmp /tmp/populated - < /tmp/definitions/swap.conf < /tmp/definitions/extra.conf < /tmp/populated2 - -cmp /tmp/populated2 - < /tmp/populated3 - -cmp /tmp/populated3 - < /testok - -exit 0 diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh index 99fd5b85b..877cbfefd 100755 --- a/test/TEST-46-HOMED/test.sh +++ b/test/TEST-46-HOMED/test.sh @@ -5,38 +5,4 @@ TEST_NO_QEMU=1 . $TEST_BASE_DIR/test-functions -test_setup() { - create_empty_image - mkdir -p $TESTDIR/root - mount ${LOOPDEV}p1 $TESTDIR/root - - ( - LOG_LEVEL=5 - eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) - - setup_basic_environment - mask_supporting_services - - # setup the testsuite service - cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < $initdir/etc/systemd/system/issue_14566_test.service << EOF -[Unit] -Description=Issue 14566 Repro - -[Service] -ExecStart=/repro.sh -ExecStopPost=/bin/true -KillMode=mixed -EOF - - cp testsuite.sh $initdir/ - cp repro.sh $initdir/ - - setup_testsuite - ) - setup_nspawn_root -} - -do_test "$@" +do_test "$@" 47 diff --git a/test/TEST-45-REPART/Makefile b/test/TEST-48-START-STOP-NO-RELOAD/Makefile similarity index 100% rename from test/TEST-45-REPART/Makefile rename to test/TEST-48-START-STOP-NO-RELOAD/Makefile diff --git a/test/TEST-48-START-STOP-NO-RELOAD/test.sh b/test/TEST-48-START-STOP-NO-RELOAD/test.sh new file mode 100755 index 000000000..f6638b324 --- /dev/null +++ b/test/TEST-48-START-STOP-NO-RELOAD/test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e +TEST_DESCRIPTION="test StartStopNoReload" +. $TEST_BASE_DIR/test-functions + +do_test "$@" 48 diff --git a/test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile b/test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh b/test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh new file mode 100755 index 000000000..249d8a22a --- /dev/null +++ b/test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +TEST_DESCRIPTION="test udev's event-timeout and timeout-signal options" +TEST_NO_NSPAWN=1 +. $TEST_BASE_DIR/test-functions + +do_test "$@" 49 diff --git a/test/TEST-50-DISSECT/Makefile b/test/TEST-50-DISSECT/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-50-DISSECT/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh new file mode 100755 index 000000000..388265805 --- /dev/null +++ b/test/TEST-50-DISSECT/test.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e +TEST_DESCRIPTION="test systemd-dissect" +IMAGE_NAME="dissect" +TEST_NO_NSPAWN=1 + +. $TEST_BASE_DIR/test-functions + +command -v mksquashfs >/dev/null 2>&1 || exit 0 +command -v veritysetup >/dev/null 2>&1 || exit 0 +command -v sfdisk >/dev/null 2>&1 || exit 0 + +# Need loop devices for systemd-dissect +test_create_image() { + create_empty_image_rootdir + + # Create what will eventually be our root filesystem onto an overlay + # If some pieces are missing from the host, skip rather than fail + ( + LOG_LEVEL=5 + setup_basic_environment + mask_supporting_services + + instmods loop =block + instmods squashfs =squashfs + instmods dm_verity =md + install_dmevent + generate_module_dependencies + inst_binary sfdisk + inst_binary losetup + + BASICTOOLS=( + bash + cat + ) + oldinitdir=$initdir + export initdir=$TESTDIR/minimal + mkdir -p $initdir + setup_basic_dirs + install_basic_tools + inst /usr/lib/os-release + ln -s ../usr/lib/os-release $initdir/etc/os-release + echo MARKER=1 >> $initdir/usr/lib/os-release + mksquashfs $initdir $oldinitdir/usr/share/minimal.raw + veritysetup format $oldinitdir/usr/share/minimal.raw $oldinitdir/usr/share/minimal.verity | grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal.roothash + export initdir=$oldinitdir + ) +} + +do_test "$@" 50 diff --git a/test/TEST-51-ISSUE-16115/Makefile b/test/TEST-51-ISSUE-16115/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-51-ISSUE-16115/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-51-ISSUE-16115/test.sh b/test/TEST-51-ISSUE-16115/test.sh new file mode 100755 index 000000000..eca235c0a --- /dev/null +++ b/test/TEST-51-ISSUE-16115/test.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +TEST_DESCRIPTION="Test ExecCondition= does not restart on abnormal or failure" +. $TEST_BASE_DIR/test-functions + +do_test "$@" 51 diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile b/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile new file mode 100644 index 000000000..a06599081 --- /dev/null +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile @@ -0,0 +1,16 @@ +BUILD_DIR=$(shell ../../tools/find-build-dir.sh) + +all setup run clean clean-again: + @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ + +# finish option is used to run checks that can only be run outside of +# the test execution. Example case, honor first shutdown, proof is obtained +# from the console output as the image shuts down. This does not show up in +# the journal so the output from the do_test is captured in a file in /tmp. +# Without the use of finish the test will still pass because if it fails +# the test will loop and will be terminated via a command timeout. +# This just provides concrete confirmation. +finish: + @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./fini.sh --$@ + +.PHONY: all setup run clean clean-again finish diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh new file mode 100755 index 000000000..993ada020 --- /dev/null +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh @@ -0,0 +1,10 @@ +#!/bin/bash +TEST_DESCRIPTION="test honor first shutdown" + +if grep -q "Shutdown is already active. Skipping emergency action request" /tmp/honorfirstshutdown.log; then + echo "$TEST_DESCRIPTION [pass]" + exit 0 +else + echo "$TEST_DESCRIPTION [fail]" + exit 1 +fi diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh new file mode 100755 index 000000000..a0848ef67 --- /dev/null +++ b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e +. $TEST_BASE_DIR/test-functions +TEST_REQUIRE_INSTALL_TESTS=0 +TEST_DESCRIPTION="testing honor first shutdown" +#INTERACTIVE_DEBUG=1 +TEST_NO_QEMU=1 + +#Using timeout because if the test fails it can loop. +# The reason is because the poweroff executed by end.service +# could turn into a reboot if the test fails. +NSPAWN_TIMEOUT=20 + +#Remove this file if it exists. this is used along with +# the make target "finish". Since concrete confirmaion is +# only found from the console during the poweroff. +rm -f /tmp/honorfirstshutdown.log >/dev/null + +do_test "$@" 52 > /tmp/honorfirstshutdown.log diff --git a/test/TEST-53-ISSUE-16347/Makefile b/test/TEST-53-ISSUE-16347/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-53-ISSUE-16347/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-53-ISSUE-16347/test.sh b/test/TEST-53-ISSUE-16347/test.sh new file mode 100755 index 000000000..089768e8d --- /dev/null +++ b/test/TEST-53-ISSUE-16347/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -e + +TEST_DESCRIPTION="test timer units when initial clock is ahead" +TEST_NO_NSPAWN=1 + +future_date=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days') +QEMU_OPTIONS="-rtc base=${future_date}" +. $TEST_BASE_DIR/test-functions + +do_test "$@" 53 diff --git a/test/basic.target b/test/basic.target deleted file mode 120000 index 061293468..000000000 --- a/test/basic.target +++ /dev/null @@ -1 +0,0 @@ -../units/basic.target \ No newline at end of file diff --git a/test/TEST-13-NSPAWN-SMOKE/create-busybox-container b/test/create-busybox-container similarity index 100% rename from test/TEST-13-NSPAWN-SMOKE/create-busybox-container rename to test/create-busybox-container diff --git a/test/fuzz/fuzz-journal-remote/oss-fuzz-21122 b/test/fuzz/fuzz-journal-remote/oss-fuzz-21122 new file mode 100644 index 000000000..e0e05e167 Binary files /dev/null and b/test/fuzz/fuzz-journal-remote/oss-fuzz-21122 differ diff --git a/test/fuzz/fuzz-json/github-15907 b/test/fuzz/fuzz-json/github-15907 new file mode 100644 index 000000000..b247ccd19 --- /dev/null +++ b/test/fuzz/fuzz-json/github-15907 @@ -0,0 +1 @@ +[7E73] \ No newline at end of file diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index b304ad0dd..7d23a9ecf 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -40,4 +40,9 @@ OtherChannels= CombinedChannels= Advertise= RxBufferSize= +RxMiniBufferSize= +RxJumboBufferSize= TxBufferSize= +RxFlowControl= +TxFlowControl= +AutoNegotiationFlowControl= diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index e8391dab7..ef1f18fa4 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -6,6 +6,7 @@ Id= GVRP= [MACVLAN] Mode= +SourceMACAddress= [WireGuard] ListenPort= PrivateKey= @@ -14,6 +15,7 @@ FwMark= FirewallMark= [MACVTAP] Mode= +SourceMACAddress= [Match] Architecture= Host= @@ -45,6 +47,7 @@ AgeingTimeSec= Priority= GroupForwardMask= VLANFiltering= +VLANProtocol= MulticastIGMPVersion= [VRF] TableId= diff --git a/test/fuzz/fuzz-netdev-parser/github-15968 b/test/fuzz/fuzz-netdev-parser/github-15968 new file mode 100644 index 000000000..0527704d2 --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/github-15968 @@ -0,0 +1,14 @@ +[NetDev] +Name=t +[L2TP] +b +Remote=1.8.0.2 + +[L2TPSession] +SessionId= +[L2TP] +PeerTunnelId=2 +[NetDev] +Kind=l2tp +[L2TP] +TunnelId=4 diff --git a/test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint b/test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint new file mode 100644 index 000000000..adff4c186 --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint @@ -0,0 +1,6 @@ +[NetDev] +Name=w +Kind=wireguard +[WireGuardPeer] +Endpoint=:0 +Endpoint=:8 \ No newline at end of file diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 2b41239b7..5d8047071 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -37,6 +37,17 @@ Unmanaged= MTUBytes= Multicast= MACAddress= +Group= +[SR-IOV] +VirtualFunction= +MACSpoofCheck= +VLANId= +VLANProtocol= +QualityOfService= +QueryReceiveSideScaling= +Trust= +LinkState= +MACAddress= [BridgeFDB] VLANId= MACAddress= @@ -73,6 +84,7 @@ UseDNS= RoutesToDNS= UseDomains= UseRoutes= +UseGateway= IAID= UserClass= UseNTP= @@ -93,20 +105,37 @@ ClientIdentifier= ListenPort= UseTimezone= RouteTable= +DenyList= BlackList= +AllowList= RequestOptions= SendRelease= MaxAttempts= IPServiceType= SendOption= +SendVendorOption= SendDecline= +MUDURL= RouteMTUBytes= +FallbackLeaseLifetimeSec= [DHCPv6] UseNTP= UseDNS= RapidCommit= ForceDHCPv6PDOtherInformation= PrefixDelegationHint= +WithoutRA= +MUDURL= +SendOption= +RequestOptions= +UserClass= +VendorClass= +SendVendorOption= +RouteMetric= +[DHCPv6PrefixDelegation] +SubnetId= +Assign= +Token= [Route] Destination= Protocol= @@ -138,6 +167,7 @@ Address= IPv6ProxyNDPAddress= IPv6AcceptRA= IPv6AcceptRouterAdvertisements= +IPv4AcceptLocal= DNSSECNegativeTrustAnchors= MACVTAP= IPv6PrivacyExtensions= @@ -169,6 +199,7 @@ VXLAN= L2TP= MACsec= LinkLocalAddressing= +IPv6LinkLocalAddressGenerationMode= ConfigureWithoutCarrier= NTP= DHCP= @@ -186,6 +217,7 @@ OnLink= PreferredLifetimeSec= AddressAutoconfiguration= ValidLifetimeSec= +Assign= [IPv6RoutePrefix] Route= LifetimeSec= @@ -193,11 +225,19 @@ LifetimeSec= EgressUntagged= VLAN= PVID= +[LLDP] +MUDURL= [CAN] SamplePoint= BitRate= +DataSamplePoint= +DataBitRate= +FDMode= +FDNonISO= RestartSec= TripleSampling= +Termination= +ListenOnly= [Address] DuplicateAddressDetection= AutoJoin= @@ -249,8 +289,10 @@ Prefix= UseDomains= RouteTable= UseDNS= +DHCPv6Client= UseAutonomousPrefix= UseOnLinkPrefix= +DenyList= BlackList= [DHCPServer] EmitNTP= @@ -261,12 +303,19 @@ EmitDNS= NTP= EmitSIP= SIP= +EmitPOP3= +POP3= +EmitSMTP= +SMTP= +EmitLPR= +LPR= EmitRouter= MaxLeaseTimeSec= DefaultLeaseTimeSec= EmitTimezone= DNS= SendOption= +SendVendorOption= [NextHop] Id= Gateway= @@ -285,7 +334,9 @@ PacketLimit= Parent= Handle= Rate= +BurstBytes= Burst= +LimitBytes= LimitSize= MTUBytes= MPUBytes= @@ -299,8 +350,10 @@ PerturbPeriodSec= Parent= Handle= PacketLimit= +MemoryLimitBytes= MemoryLimit= Flows= +QuantumBytes= Quantum= TargetSec= IntervalSec= @@ -311,7 +364,9 @@ Parent= Handle= PacketLimit= FlowLimit= +QuantumBytes= Quantum= +InitialQuantumBytes= InitialQuantum= MaximumRate= Buckets= @@ -326,6 +381,11 @@ TargetSec= IntervalSec= CEThresholdSec= ECN= +[CAKE] +Parent= +Handle= +Bandwidth= +OverheadBytes= [TrafficControlQueueingDiscipline] Parent= NetworkEmulatorDelaySec= @@ -337,3 +397,74 @@ NetworkEmulatorPacketLimit= Parent= Handle= Id= +[HierarchyTokenBucket] +Parent= +Handle= +DefaultClass= +RateToQuantum= +[HierarchyTokenBucketClass] +Parent= +ClassId= +Priority= +QuantumBytes= +MTUBytes= +OverheadBytes= +Rate= +CeilRate= +BufferBytes= +CeilBufferBytes= +[BFIFO] +Parent= +Handle= +LimitBytes= +[PFIFO] +Parent= +Handle= +PacketLimit= +[PFIFOHeadDrop] +Parent= +Handle= +PacketLimit= +[PFIFOFast] +Parent= +Handle= +[GenericRandomEarlyDetection] +Parent= +Handle= +VirtualQueues= +DefaultVirtualQueue= +GenericRIO= +[StochasticFairBlue] +Parent= +Handle= +PacketLimit= +[PIE] +Parent= +Handle= +PacketLimit= +[QuickFairQueueing] +Parent= +Handle= +[QuickFairQueueingClass] +Parent= +ClassId= +Weight= +MaxPacketBytes= +[DeficitRoundRobinScheduler] +Parent= +Handle= +[DeficitRoundRobinSchedulerClass] +Parent= +ClassId= +QuantumBytes= +[EnhancedTransmissionSelection] +Parent= +Handle= +Bands= +StrictBands= +QuantumBytes= +PriorityMap= +[HeavyHitterFilter] +Parent= +Handle= +PacketLimit= diff --git a/test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network b/test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network new file mode 100644 index 000000000..ed7bdabfd --- /dev/null +++ b/test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network @@ -0,0 +1,2 @@ +[Network] +DNSSECNegativeTrustAnchors=i i \ No newline at end of file diff --git a/test/fuzz/fuzz-network-parser/github-15885 b/test/fuzz/fuzz-network-parser/github-15885 new file mode 100644 index 000000000..9bbdcb29e --- /dev/null +++ b/test/fuzz/fuzz-network-parser/github-15885 @@ -0,0 +1,9 @@ +[DHCPv4] +SendOption=1:string: +SendOption=1:uint8: +SendOption=1:uint16: +SendOption=1:uint32: +SendOption=1:ipv4address: +SendOption=1:ipv4address:127.0.0.1 +SendOption=1:ipv6address: +SendOption=1:ipv6address:52:54:00:b9:b5:61 diff --git a/test/fuzz/fuzz-network-parser/github-15951 b/test/fuzz/fuzz-network-parser/github-15951 new file mode 100644 index 000000000..7785f35a4 --- /dev/null +++ b/test/fuzz/fuzz-network-parser/github-15951 @@ -0,0 +1,2 @@ +[DHCPServer] +POP3Servers=1.8.5.0 diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-15678 b/test/fuzz/fuzz-network-parser/oss-fuzz-15678 index 2a37d5f45..fb7cfcfb3 100644 --- a/test/fuzz/fuzz-network-parser/oss-fuzz-15678 +++ b/test/fuzz/fuzz-network-parser/oss-fuzz-15678 @@ -1,2 +1,2 @@ [IPv6AcceptRA] -BlackList=70:: 70:: \ No newline at end of file +DenyList=70:: 70:: \ No newline at end of file diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-23895 b/test/fuzz/fuzz-network-parser/oss-fuzz-23895 new file mode 100644 index 000000000..a86361d0b Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-23895 differ diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-23950 b/test/fuzz/fuzz-network-parser/oss-fuzz-23950 new file mode 100644 index 000000000..5bfb17ba3 Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-23950 differ diff --git a/test/fuzz/fuzz-udev-rules/line-too-long b/test/fuzz/fuzz-udev-rules/line-too-long new file mode 100644 index 000000000..c0b908dbb --- /dev/null +++ b/test/fuzz/fuzz-udev-rules/line-too-long @@ -0,0 +1 @@ +O\x4294967296d%d;ycalc\x0a]r\n%s$(xcalc)$1'xcalc!xcalc%s"strjng_escaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSYMLIaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index 45f875197..dbff9ab2c 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -44,6 +44,7 @@ BlockIOWeight= BlockIOWriteBandwidth= Broadcast= BusName= +CoredumpFilter= CPUAccounting= CPUQuota= CPUShares= @@ -163,6 +164,7 @@ PIDFile= PartOf= PassCredentials= PassSecurity= +PassPacketInfo= PathChanged= PathExists= PathExistsGlob= @@ -194,6 +196,9 @@ ReusePort= RootDirectory= RootDirectoryStartOnly= RootImage= +RootHash= +RootHashSignature= +RootVerity= RuntimeMaxSec= SELinuxContextFromNet= SecureBits= @@ -855,6 +860,7 @@ RateLimitIntervalSec= ReadKMsg= ReadOnly= ReadOnlyPaths= +ReadWriteOnly= ReadWritePaths= RemoveIPC= ReserveVT= @@ -863,6 +869,7 @@ RestrictNamespaces= RestrictRealtime= RestrictSUIDSGID= RuntimeDirectory= +RuntimeDirectoryInodesMax= RuntimeDirectoryMode= RuntimeDirectoryPreserve= RuntimeDirectorySize= diff --git a/test/fuzz/fuzz-xdg-desktop/full.desktop b/test/fuzz/fuzz-xdg-desktop/full.desktop new file mode 100644 index 000000000..e5da36bba --- /dev/null +++ b/test/fuzz/fuzz-xdg-desktop/full.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=GNOME Settings Daemon's power plugin +Exec=/usr/bin/sleep %i %f "%F" "--test" ";\\\\!?" +OnlyShowIn=GNOME; +NoDisplay=true +X-GNOME-Autostart-Phase=Initialization +X-GNOME-Autostart-Notify=true +X-GNOME-AutoRestart=true +X-GNOME-HiddenUnderSystemd=true diff --git a/test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop b/test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop new file mode 100644 index 000000000..9d3e0c5a0 --- /dev/null +++ b/test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=GNOME Settings Daemon's power plugin +Exec=/usr/libexec/gsd-power +OnlyShowIn=GNOME; +NoDisplay=true +X-GNOME-Autostart-Phase=Initialization +X-GNOME-Autostart-Notify=true +X-GNOME-AutoRestart=true +X-GNOME-HiddenUnderSystemd=true diff --git a/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 b/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 new file mode 100644 index 000000000..4b4cadffb Binary files /dev/null and b/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 differ diff --git a/test/fuzz/fuzz-xdg-desktop/valid.desktop b/test/fuzz/fuzz-xdg-desktop/valid.desktop new file mode 100644 index 000000000..20fb5fecb --- /dev/null +++ b/test/fuzz/fuzz-xdg-desktop/valid.desktop @@ -0,0 +1,12 @@ +Desktop Entry +Name= +Exec= +TryExec= +Type= +OnlyShowIn= +NotShowIn= +Hidden= +AutostartCondition= +X-KDE-autostart-condition= +X-GNOME-Autostart-Phase= +X-GNOME-HiddenUnderSystemd= diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build index c514f57fe..99584c144 100644 --- a/test/fuzz/meson.build +++ b/test/fuzz/meson.build @@ -1,17 +1,25 @@ # SPDX-License-Identifier: LGPL-2.1+ -sanitize_address = custom_target( - 'sanitize-address-fuzzers', - output : 'sanitize-address-fuzzers', +# The 'optimization' option was introduced in meson 0.48.0, so let's keep +# the code compatible with older versions as well +if meson.version().version_compare('>=0.48.0') + optimization = '--optimization=@0@'.format(get_option('optimization')) +else + optimization = '' +endif + +sanitize_address_undefined = custom_target( + 'sanitize-address-undefined-fuzzers', + output : 'sanitize-address-undefined-fuzzers', command : [meson_build_sh, project_source_root, '@OUTPUT@', 'fuzzers', - '-Db_lundef=false -Db_sanitize=address', + '-Db_lundef=false -Db_sanitize=address,undefined @0@'.format(optimization), ' '.join(cc.cmd_array()), cxx_cmd]) -sanitizers = [['address', sanitize_address]] +sanitizers = [['address,undefined', sanitize_address_undefined]] if git.found() out = run_command( diff --git a/test/loopy2.service b/test/loopy2.service deleted file mode 120000 index 961b1fe9b..000000000 --- a/test/loopy2.service +++ /dev/null @@ -1 +0,0 @@ -loopy.service \ No newline at end of file diff --git a/test/loopy4.service b/test/loopy4.service deleted file mode 120000 index 43e5658bc..000000000 --- a/test/loopy4.service +++ /dev/null @@ -1 +0,0 @@ -loopy3.service \ No newline at end of file diff --git a/test/meson.build b/test/meson.build index 2fbea31cc..5d9fb5ab5 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,237 +1,61 @@ # SPDX-License-Identifier: LGPL-2.1+ -test_data_files = ''' - a.service - a-conj.service - b.service - basic.target - c.service - d.service - daughter.service - dml.slice - dml-passthrough.slice - dml-passthrough-empty.service - dml-passthrough-set-dml.service - dml-passthrough-set-ml.service - dml-override.slice - dml-override-empty.service - dml-discard.slice - dml-discard-empty.service - dml-discard-set-ml.service - e.service - end.service - f.service - g.service - grandchild.service - h.service - hello-after-sleep.target - hello.service - hwdb.d/10-bad.hwdb - i.service - journal-data/journal-1.txt - journal-data/journal-2.txt - nomem.slice - nomemleaf.service - parent-deep.slice - parent.slice - sched_idle_bad.service - sched_idle_ok.service - sched_rr_bad.service - sched_rr_change.service - sched_rr_ok.service - shutdown.target - sleep.service - sockets.target - son.service - sysinit.target - test-execute/exec-basic.service - test-execute/exec-ambientcapabilities-merge-nfsnobody.service - test-execute/exec-ambientcapabilities-merge-nobody.service - test-execute/exec-ambientcapabilities-merge.service - test-execute/exec-ambientcapabilities-nfsnobody.service - test-execute/exec-ambientcapabilities-nobody.service - test-execute/exec-ambientcapabilities.service - test-execute/exec-bindpaths.service - test-execute/exec-capabilityboundingset-invert.service - test-execute/exec-capabilityboundingset-merge.service - test-execute/exec-capabilityboundingset-reset.service - test-execute/exec-capabilityboundingset-simple.service - test-execute/exec-condition-failed.service - test-execute/exec-condition-skip.service - test-execute/exec-cpuaffinity1.service - test-execute/exec-cpuaffinity2.service - test-execute/exec-cpuaffinity3.service - test-execute/exec-dynamicuser-fixeduser-adm.service - test-execute/exec-dynamicuser-fixeduser-games.service - test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service - test-execute/exec-dynamicuser-fixeduser.service - test-execute/exec-dynamicuser-statedir-migrate-step1.service - test-execute/exec-dynamicuser-statedir-migrate-step2.service - test-execute/exec-dynamicuser-statedir.service - test-execute/exec-dynamicuser-supplementarygroups.service - test-execute/exec-environment-no-substitute.service - test-execute/exec-environment-empty.service - test-execute/exec-environment-multiple.service - test-execute/exec-environment.service - test-execute/exec-environmentfile.service - test-execute/exec-group-nfsnobody.service - test-execute/exec-group-nobody.service - test-execute/exec-group-nogroup.service - test-execute/exec-group.service - test-execute/exec-ignoresigpipe-no.service - test-execute/exec-ignoresigpipe-yes.service - test-execute/exec-inaccessiblepaths-mount-propagation.service - test-execute/exec-inaccessiblepaths-sys.service - test-execute/exec-ioschedulingclass-best-effort.service - test-execute/exec-ioschedulingclass-idle.service - test-execute/exec-ioschedulingclass-none.service - test-execute/exec-ioschedulingclass-realtime.service - test-execute/exec-oomscoreadjust-negative.service - test-execute/exec-oomscoreadjust-positive.service - test-execute/exec-passenvironment-absent.service - test-execute/exec-passenvironment-empty.service - test-execute/exec-passenvironment-repeated.service - test-execute/exec-passenvironment.service - test-execute/exec-personality-aarch64.service - test-execute/exec-personality-ppc64.service - test-execute/exec-personality-ppc64le.service - test-execute/exec-personality-s390.service - test-execute/exec-personality-x86-64.service - test-execute/exec-personality-x86.service - test-execute/exec-privatedevices-disabled-by-prefix.service - test-execute/exec-privatedevices-no-capability-mknod.service - test-execute/exec-privatedevices-no-capability-sys-rawio.service - test-execute/exec-privatedevices-no.service - test-execute/exec-privatedevices-yes-with-group.service - test-execute/exec-privatedevices-yes-capability-mknod.service - test-execute/exec-privatedevices-yes-capability-sys-rawio.service - test-execute/exec-privatedevices-yes.service - test-execute/exec-privatenetwork-yes.service - test-execute/exec-privatetmp-no.service - test-execute/exec-privatetmp-yes.service - test-execute/exec-privatetmp-disabled-by-prefix.service - test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service - test-execute/exec-protectkernellogs-yes-capabilities.service - test-execute/exec-protectkernellogs-no-capabilities.service - test-execute/exec-protectkernelmodules-no-capabilities.service - test-execute/exec-protectkernelmodules-yes-capabilities.service - test-execute/exec-protectkernelmodules-yes-mount-propagation.service - test-execute/exec-readonlypaths-mount-propagation.service - test-execute/exec-readonlypaths-simple.service - test-execute/exec-readonlypaths-with-bindpaths.service - test-execute/exec-readonlypaths.service - test-execute/exec-readwritepaths-mount-propagation.service - test-execute/exec-restrictnamespaces-merge-all.service - test-execute/exec-restrictnamespaces-merge-and.service - test-execute/exec-restrictnamespaces-merge-or.service - test-execute/exec-restrictnamespaces-mnt-blacklist.service - test-execute/exec-restrictnamespaces-mnt.service - test-execute/exec-restrictnamespaces-no.service - test-execute/exec-restrictnamespaces-yes.service - test-execute/exec-runtimedirectory-mode.service - test-execute/exec-runtimedirectory-owner-nfsnobody.service - test-execute/exec-runtimedirectory-owner-nobody.service - test-execute/exec-runtimedirectory-owner-nogroup.service - test-execute/exec-runtimedirectory-owner.service - test-execute/exec-runtimedirectory.service - test-execute/exec-specifier-interpolation.service - test-execute/exec-specifier.service - test-execute/exec-specifier@.service - test-execute/exec-standardinput-data.service - test-execute/exec-standardinput-file.service - test-execute/exec-standardinput-file-cat.service - test-execute/exec-standardoutput-file.service - test-execute/exec-standardoutput-append.service - test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service - test-execute/exec-supplementarygroups-multiple-groups-withgid.service - test-execute/exec-supplementarygroups-multiple-groups-withuid.service - test-execute/exec-supplementarygroups-single-group-user.service - test-execute/exec-supplementarygroups-single-group.service - test-execute/exec-supplementarygroups.service - test-execute/exec-systemcallerrornumber-name.service - test-execute/exec-systemcallerrornumber-number.service - test-execute/exec-systemcallfilter-failing.service - test-execute/exec-systemcallfilter-failing2.service - test-execute/exec-systemcallfilter-not-failing.service - test-execute/exec-systemcallfilter-not-failing2.service - test-execute/exec-systemcallfilter-system-user-nfsnobody.service - test-execute/exec-systemcallfilter-system-user-nobody.service - test-execute/exec-systemcallfilter-system-user.service - test-execute/exec-systemcallfilter-with-errno-multi.service - test-execute/exec-systemcallfilter-with-errno-name.service - test-execute/exec-systemcallfilter-with-errno-number.service - test-execute/exec-temporaryfilesystem-options.service - test-execute/exec-temporaryfilesystem-ro.service - test-execute/exec-temporaryfilesystem-rw.service - test-execute/exec-temporaryfilesystem-usr.service - test-execute/exec-umask-0177.service - test-execute/exec-umask-default.service - test-execute/exec-unsetenvironment.service - test-execute/exec-user-nfsnobody.service - test-execute/exec-user-nobody.service - test-execute/exec-user.service - test-execute/exec-workingdirectory.service - test-execute/exec-workingdirectory-trailing-dot.service - test-path/basic.target - test-path/path-changed.path - test-path/path-changed.service - test-path/path-directorynotempty.path - test-path/path-directorynotempty.service - test-path/path-exists.path - test-path/path-exists.service - test-path/path-existsglob.path - test-path/path-existsglob.service - test-path/path-makedirectory.path - test-path/path-makedirectory.service - test-path/path-modified.path - test-path/path-modified.service - test-path/path-mycustomunit.service - test-path/path-service.service - test-path/path-unit.path - test-path/paths.target - test-path/sysinit.target - test-umount/empty.mountinfo - test-umount/example.swaps - test-umount/garbled.mountinfo - test-umount/rhbug-1554943.mountinfo - testsuite.target - timers.target - unit-with-.service.d/20-override.conf - unit-with-multiple-.service.d/20-override.conf - unit-with-multiple-.service.d/30-override.conf - unit-with-multiple-dashes.service - unit-with-multiple-dashes.service.d/10-override.conf - unstoppable.service -'''.split() - -if conf.get('ENABLE_RESOLVE') == 1 - test_data_files += ''' - test-resolve/_openpgpkey.fedoraproject.org.pkts - test-resolve/fedoraproject.org.pkts - test-resolve/gandi.net.pkts - test-resolve/google.com.pkts - test-resolve/root.pkts - test-resolve/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts - test-resolve/teamits.com.pkts - test-resolve/zbyszek@fedoraproject.org.pkts - test-resolve/_443._tcp.fedoraproject.org.pkts - test-resolve/kyhwana.org.pkts - test-resolve/fake-caa.pkts - '''.split() -endif - if install_tests - foreach file : test_data_files - subdir = file.split('/')[0] - if subdir == file - subdir = '' - endif + testdata_dir = testsdir + '/testdata/' - install_data(file, - install_dir : testsdir + '/testdata/' + subdir) - endforeach + install_subdir('journal-data', + install_dir : testdata_dir) + install_subdir('units', + install_dir : testdata_dir) + install_subdir('test-execute', + install_dir : testdata_dir) + install_subdir('test-path', + install_dir : testdata_dir) + install_subdir('test-umount', + install_dir : testdata_dir) + install_subdir('test-network-generator-conversion', + install_dir : testdata_dir) + install_subdir('testsuite-04.units', + install_dir : testdata_dir) + install_subdir('testsuite-06.units', + install_dir : testdata_dir) + install_subdir('testsuite-10.units', + install_dir : testdata_dir) + install_subdir('testsuite-11.units', + install_dir : testdata_dir) + install_subdir('testsuite-16.units', + install_dir : testdata_dir) + install_subdir('testsuite-28.units', + install_dir : testdata_dir) + install_subdir('testsuite-30.units', + install_dir : testdata_dir) + install_subdir('testsuite-52.units', + install_dir : testdata_dir) + + testsuite08_dir = testdata_dir + '/testsuite-08.units' + install_data('testsuite-08.units/-.mount', + install_dir : testsuite08_dir) + install_data('testsuite-08.units/systemd-remount-fs.service', + install_dir : testsuite08_dir) + meson.add_install_script(meson_make_symlink, + './-.mount', + testsuite08_dir + '/root.mount') + meson.add_install_script(meson_make_symlink, + '../-.mount', + testsuite08_dir + '/local-fs.target.wants/-.mount') + + if conf.get('ENABLE_RESOLVE') == 1 + install_subdir('test-resolve', + install_dir : testdata_dir) + endif + + install_data('create-busybox-container', + install_mode : 'rwxr-xr-x', + install_dir : testdata_dir) endif +test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh') + ############################################################ rule_syntax_check_py = find_program('rule-syntax-check.py') @@ -257,6 +81,9 @@ if install_tests install_data('run-unit-tests.py', install_mode : 'rwxr-xr-x', install_dir : testsdir) + install_data('test-network-generator-conversion.sh', + install_mode : 'rwxr-xr-x', + install_dir : testsdir) endif ############################################################ diff --git a/test/networkd-test.py b/test/networkd-test.py index 618237161..b22569481 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -376,7 +376,7 @@ DHCP={} # IPv6, but we want to wait for both for _ in range(10): out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.iface]) - if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out: + if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out and b'tentative' not in out: break time.sleep(1) else: diff --git a/test/run-integration-tests.sh b/test/run-integration-tests.sh index c0a8448a8..ac7a28cf2 100755 --- a/test/run-integration-tests.sh +++ b/test/run-integration-tests.sh @@ -5,41 +5,71 @@ BUILD_DIR="$($(dirname "$0")/../tools/find-build-dir.sh)" if [ $# -gt 0 ]; then args="$@" else - args="clean setup run clean-again" + args="setup run clean-again" fi +args_no_clean=$(sed -r 's/(^| )clean($| )/ /g' <<<$args) +do_clean=$( [ "$args" = "$args_no_clean" ]; echo $? ) ninja -C "$BUILD_DIR" declare -A results +declare -A times COUNT=0 FAILURES=0 cd "$(dirname "$0")" + +# Let's always do the cleaning operation first, because it destroys the image +# cache. +if [ $do_clean = 1 ]; then + for TEST in TEST-??-* ; do + ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" clean ) + done +fi + +pass_blacklist() { + for marker in $BLACKLIST_MARKERS; do + if [ -f "$1/$marker" ]; then + echo "========== BLACKLISTED: $1 ($marker) ==========" + return 1 + fi + done + return 0 +} + for TEST in TEST-??-* ; do COUNT=$(($COUNT+1)) + pass_blacklist $TEST || continue + start=$(date +%s) + echo -e "\n--x-- Running $TEST --x--" set +e - ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" $args ) + ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" $args_no_clean ) RESULT=$? set -e echo "--x-- Result of $TEST: $RESULT --x--" results["$TEST"]="$RESULT" + times["$TEST"]=$(( $(date +%s) - $start )) [ "$RESULT" -ne "0" ] && FAILURES=$(($FAILURES+1)) done +if [ $FAILURES -eq 0 -a $do_clean = 1 ]; then + for TEST in ${!results[@]}; do + ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" clean-again ) + done +fi + echo "" for TEST in ${!results[@]}; do RESULT="${results[$TEST]}" - if [ "$RESULT" -eq "0" ] ; then - echo "$TEST: SUCCESS" - else - echo "$TEST: FAIL" - fi + time="${times[$TEST]}" + string=$([ "$RESULT" = "0" ] && echo "SUCCESS" || echo "FAIL") + printf "%-35s %-8s (%3s s)\n" "${TEST}:" "${string}" "$time" done | sort if [ "$FAILURES" -eq 0 ] ; then diff --git a/test/shutdown.target b/test/shutdown.target deleted file mode 120000 index 1a3c2eec8..000000000 --- a/test/shutdown.target +++ /dev/null @@ -1 +0,0 @@ -../units/shutdown.target \ No newline at end of file diff --git a/test/sockets.target b/test/sockets.target deleted file mode 120000 index 8ff86a077..000000000 --- a/test/sockets.target +++ /dev/null @@ -1 +0,0 @@ -../units/sockets.target \ No newline at end of file diff --git a/test/sysinit.target b/test/sysinit.target deleted file mode 120000 index 330133818..000000000 --- a/test/sysinit.target +++ /dev/null @@ -1 +0,0 @@ -../units/sysinit.target \ No newline at end of file diff --git a/test/test-execute/exec-restrictnamespaces-mnt-blacklist.service b/test/test-execute/exec-restrictnamespaces-mnt-deny-list.service similarity index 100% rename from test/test-execute/exec-restrictnamespaces-mnt-blacklist.service rename to test/test-execute/exec-restrictnamespaces-mnt-deny-list.service diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service index 7c3f81f2b..a58abc68d 100644 --- a/test/test-execute/exec-specifier.service +++ b/test/test-execute/exec-specifier.service @@ -27,5 +27,5 @@ ExecStart=test %h = /root ExecStart=sh -c 'test %s = /bin/sh' ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)' ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')' -ExecStart=sh -c 'test %H = $$(hostname)' +ExecStart=sh -c 'test %H = $$(uname -n)' ExecStart=sh -c 'test %v = $$(uname -r)' diff --git a/test/test-execute/exec-specifier@.service b/test/test-execute/exec-specifier@.service index a38892684..faecbf37b 100644 --- a/test/test-execute/exec-specifier@.service +++ b/test/test-execute/exec-specifier@.service @@ -25,5 +25,5 @@ ExecStart=test %h = /root ExecStart=sh -c 'test %s = /bin/sh' ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)' ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')' -ExecStart=sh -c 'test %H = $$(hostname)' +ExecStart=sh -c 'test %H = $$(uname -n)' ExecStart=sh -c 'test %v = $$(uname -r)' diff --git a/test/test-functions b/test/test-functions index 0876ed3be..863cc18ae 100644 --- a/test/test-functions +++ b/test/test-functions @@ -14,8 +14,12 @@ NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}" TIMED_OUT= # will be 1 after run_* if *_TIMEOUT is set and test timed out [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}" UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}" -EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)" +EFI_MOUNT="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}" QEMU_MEM="${QEMU_MEM:-512M}" +IMAGE_NAME=${IMAGE_NAME:-default} +TEST_REQUIRE_INSTALL_TESTS="${TEST_REQUIRE_INSTALL_TESTS:-1}" +TEST_PARALLELIZE="${TEST_PARALLELIZE:-0}" +LOOPDEV= # Decide if we can (and want to) run QEMU with KVM acceleration. # Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not, @@ -35,15 +39,111 @@ fi PATH_TO_INIT=$ROOTLIBDIR/systemd [ "$SYSTEMD_JOURNALD" ] || SYSTEMD_JOURNALD=$(which -a $BUILD_DIR/systemd-journald $ROOTLIBDIR/systemd-journald 2>/dev/null | grep '^/' -m1) +[ "$SYSTEMD_JOURNAL_REMOTE" ] || SYSTEMD_JOURNAL_REMOTE=$(which -a $BUILD_DIR/systemd-journal-remote $ROOTLIBDIR/systemd-journal-remote 2>/dev/null | grep '^/' -m1) [ "$SYSTEMD" ] || SYSTEMD=$(which -a $BUILD_DIR/systemd $ROOTLIBDIR/systemd 2>/dev/null | grep '^/' -m1) [ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1) [ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1) -BASICTOOLS="test env sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs" -DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv" +BASICTOOLS=( + awk + basename + bash + busybox + capsh + cat + chmod + chown + cmp + cryptsetup + cut + date + dd + diff + dirname + dmsetup + echo + env + false + getent + getfacl + grep + gunzip + gzip + head + ionice + ip + ln + loadkeys + login + lz4cat + mkfifo + mktemp + modprobe + mount + mountpoint + mv + nc + nproc + readlink + rev + rm + rmdir + sed + seq + setfont + setsid + sfdisk + sh + sleep + socat + stat + su + sulogin + sysctl + tail + tar + tee + test + timeout + touch + tr + true + truncate + umount + uname + unshare + xargs + xzcat +) + +DEBUGTOOLS=( + cp + df + dhclient + dmesg + du + find + free + grep + hostname + id + less + ln + ls + mkdir + ping + ps + route + sort + strace + stty + tty + vi +) STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" STATEFILE="$STATEDIR/.testdir" +IMAGESTATEDIR="$STATEDIR/.." TESTLOG="$STATEDIR/test.log" is_built_with_asan() { @@ -138,6 +238,11 @@ run_qemu() { CONSOLE=ttyS0 + rm -f "$initdir"/{testok,failed,skipped} + # make sure the initdir is not mounted to avoid concurrent access + cleanup_initdir + umount_loopback + if [[ ! "$KERNEL_BIN" ]]; then if [[ "$LOOKS_LIKE_ARCH" ]]; then KERNEL_BIN=/boot/vmlinuz-linux @@ -184,6 +289,9 @@ run_qemu() { find_qemu_bin || return 1 + # Umount initdir to avoid concurrent access to the filesystem + _umount_dir $initdir + local _cgroup_args if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then _cgroup_args="systemd.unified_cgroup_hierarchy=yes" @@ -198,14 +306,18 @@ run_qemu() { if [[ "$LOOKS_LIKE_SUSE" ]]; then PARAMS+="rd.hostonly=0" - elif [[ "$LOOKS_LIKE_ARCH" ]]; then - PARAMS+="rw" + fi + + local _end + if [[ ! "$INTERACTIVE_DEBUG" ]]; then + _end="systemd.wants=end.service" else - PARAMS+="ro" + _end="" fi KERNEL_APPEND="$PARAMS \ root=/dev/sda1 \ +rw \ raid=noautodetect \ rd.luks=0 \ loglevel=2 \ @@ -213,15 +325,19 @@ init=$PATH_TO_INIT \ console=$CONSOLE \ selinux=0 \ $_cgroup_args \ +SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units: \ +systemd.unit=testsuite.target \ +systemd.wants=testsuite-$1.service ${_end} \ $KERNEL_APPEND \ " + [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC" QEMU_OPTIONS="-smp $QEMU_SMP \ -net none \ -m $QEMU_MEM \ -nographic \ -kernel $KERNEL_BIN \ --drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \ +-drive format=raw,cache=unsafe,file=$image \ $QEMU_OPTIONS \ " @@ -252,25 +368,43 @@ $QEMU_OPTIONS \ # success), or 1 if nspawn is not available. run_nspawn() { [[ -d /run/systemd/system ]] || return 1 + rm -f "$initdir"/{testok,failed,skipped} - local _nspawn_cmd="$SYSTEMD_NSPAWN $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND" + local _nspawn_cmd=( + --register=no + --kill-signal=SIGKILL + --directory=$1 + --setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units: + $PATH_TO_INIT + $KERNEL_APPEND + systemd.unit=testsuite.target + systemd.wants=testsuite-$2.service + ) + + if [[ ! "$INTERACTIVE_DEBUG" ]]; then + _nspawn_cmd+=( systemd.wants=end.service ) + fi + + local _nspawn_pre if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then - _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd" + _nspawn_pre=(timeout --foreground $NSPAWN_TIMEOUT) + else + _nspawn_pre=() fi if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping" exit elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then - _nspawn_cmd="env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd" + _nspawn_pre=("${_nspawn_pre[@]}" env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY) elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then - _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY $_nspawn_cmd" + _nspawn_pre=("${_nspawn_pre[@]}" env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY) else dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]" exit 1 fi - (set -x; $_nspawn_cmd) + (set -x; "${_nspawn_pre[@]}" "$SYSTEMD_NSPAWN" $NSPAWN_ARGUMENTS "${_nspawn_cmd[@]}") rc=$? if [ "$rc" = 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then derror "test timed out after $NSPAWN_TIMEOUT s" @@ -288,6 +422,7 @@ setup_basic_environment() { install_systemd install_missing_libraries install_config_files + install_zoneinfo create_rc_local install_basic_tools install_libnss @@ -301,6 +436,8 @@ setup_basic_environment() { install_plymouth install_debug_tools install_ld_so_conf + install_testuser + has_user_dbus_socket && install_user_dbus setup_selinux strip_binaries install_depmod_files @@ -326,27 +463,9 @@ setup_selinux() { exit 1 fi - cat <$initdir/etc/systemd/system/autorelabel.service -[Unit] -Description=Relabel all filesystems -DefaultDependencies=no -Requires=local-fs.target -Conflicts=shutdown.target -After=local-fs.target -Before=sysinit.target shutdown.target -ConditionSecurity=selinux -ConditionPathExists=|/.autorelabel - -[Service] -ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot' -Type=oneshot -TimeoutSec=0 -RemainAfterExit=yes -EOF - touch $initdir/.autorelabel - mkdir -p $initdir/etc/systemd/system/basic.target.wants - ln -fs autorelabel.service $initdir/etc/systemd/system/basic.target.wants/autorelabel.service + mkdir -p $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants + ln -sf ../autorelabel.service $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/ dracut_install $_fixfiles_tools dracut_install fixfiles @@ -454,7 +573,7 @@ printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CON # under ASan+UBSan in containers, which, in turn, are run in VMs. # Let's limit which environments such services should be executed in. mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d -printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf +printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf # Let's override another hard-coded timeout that kicks in too early mkdir -p /etc/systemd/system/systemd-journal-flush.service.d @@ -475,7 +594,7 @@ unset_ld_preload() { } unset_ld_preload systemd-remount-fs -unset_ld_preload testsuite +unset_ld_preload testsuite- export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS exec $ROOTLIBDIR/systemd "\$@" @@ -514,6 +633,9 @@ install_dmevent() { else inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules fi + if [[ "$LOOKS_LIKE_SUSE" ]]; then + inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules + fi } install_systemd() { @@ -530,7 +652,7 @@ install_systemd() { # and it could fill the available space strip_binaries - [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse + [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse # enable debug logging in PID1 echo LogLevel=debug >> $initdir/etc/systemd/system.conf @@ -556,17 +678,35 @@ install_missing_libraries() { done } +cleanup_loopdev() { + if [ -n "${LOOPDEV}" ]; then + ddebug "losetup -d $LOOPDEV" + losetup -d "${LOOPDEV}" + unset LOOPDEV + fi +} + +trap cleanup_loopdev EXIT INT QUIT PIPE + create_empty_image() { + if [ -z "$IMAGE_NAME" ]; then + echo "create_empty_image: \$IMAGE_NAME not set" + exit 1 + fi + local _size=500 if [[ "$STRIP_BINARIES" = "no" ]]; then _size=$((4*_size)) fi - rm -f "$TESTDIR/rootdisk.img" + + echo "Setting up $IMAGE_PUBLIC (${_size} MB)" + rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC" + # Create the blank file to use as a root filesystem - truncate -s "${_size}M" "$TESTDIR/rootdisk.img" - LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img) + truncate -s "${_size}M" "$IMAGE_PUBLIC" + + LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC") [ -b "$LOOPDEV" ] || return 1 - echo "LOOPDEV=$LOOPDEV" >> $STATEFILE sfdisk "$LOOPDEV" <$initdir/etc/sysusers.d/testuser.conf < $initdir/etc/environment > $initdir/etc/machine-id + # set the hostname echo systemd-testsuite > $initdir/etc/hostname - # fstab - if [[ "$LOOKS_LIKE_SUSE" ]]; then - ROOTMOUNT="/dev/sda1 / ${FSTYPE} rw 0 1" - else - ROOTMOUNT="LABEL=systemd / ${FSTYPE} rw 0 1" - fi - cat >$initdir/etc/fstab <$initdir/etc/systemd/system.conf.d/status.conf + fi } install_basic_tools() { - [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS + dracut_install "${BASICTOOLS[@]}" dracut_install -o sushell # in Debian ldconfig is just a shell script wrapper around ldconfig.real dracut_install -o ldconfig.real } install_debug_tools() { - [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS + dracut_install "${DEBUGTOOLS[@]}" if [[ $INTERACTIVE_DEBUG ]]; then # Set default TERM from vt220 to linux, so at least basic key shortcuts work @@ -810,6 +1011,19 @@ install_dbus() { | while read file; do inst $file done + + # setup policy for Type=dbus test + mkdir -p $initdir/etc/dbus-1/system.d + cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf < + + + + + + +EOF } install_user_dbus() { @@ -885,10 +1099,16 @@ install_keymaps() { } install_zoneinfo() { - for i in /usr/share/zoneinfo/{,*/,*/*/}*; do - [[ -f $i ]] || continue - inst $i - done + inst_any /usr/share/zoneinfo/Asia/Seoul + inst_any /usr/share/zoneinfo/Asia/Vladivostok + inst_any /usr/share/zoneinfo/Australia/Sydney + inst_any /usr/share/zoneinfo/Europe/Berlin + inst_any /usr/share/zoneinfo/Europe/Kiev + inst_any /usr/share/zoneinfo/Pacific/Auckland + inst_any /usr/share/zoneinfo/Pacific/Honolulu + inst_any /usr/share/zoneinfo/CET + inst_any /usr/share/zoneinfo/EET + inst_any /usr/share/zoneinfo/UTC } install_fonts() { @@ -916,40 +1136,17 @@ has_user_dbus_socket() { fi } -enable_user_manager() { - has_user_dbus_socket || return 0 - - local _userid - [[ $# -gt 0 ]] || set -- nobody - mkdir -p "$initdir/var/lib/systemd/linger" - for _userid; do - touch "$initdir/var/lib/systemd/linger/$_userid" - done - dracut_install su - install_user_dbus -} - -setup_testsuite() { - cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/ - cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/ - - mkdir -p $initdir/etc/systemd/system/testsuite.target.wants - ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service - # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set - [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service - - # make the testsuite the default target - ln -fs testsuite.target $initdir/etc/systemd/system/default.target -} - setup_nspawn_root() { - rm -fr $TESTDIR/nspawn-root - ddebug "cp -ar $initdir $TESTDIR/nspawn-root" - cp -ar $initdir $TESTDIR/nspawn-root - # we don't mount in the nspawn root - rm -f $TESTDIR/nspawn-root/etc/fstab + if [ -z "${initdir}" ]; then + dfatal "\$initdir not defined" + exit 1 + fi + + rm -rf "$TESTDIR/unprivileged-nspawn-root" + if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then - cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root + ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root" + cp -ar $initdir $TESTDIR/unprivileged-nspawn-root fi } @@ -1005,7 +1202,10 @@ inst_libs() { } import_testdir() { + # make sure we don't get a stale LOOPDEV value from old times + __LOOPDEV=$LOOPDEV [[ -e $STATEFILE ]] && . $STATEFILE + LOOPDEV=$__LOOPDEV if [[ ! -d "$TESTDIR" ]]; then if [[ -z "$TESTDIR" ]]; then TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX) @@ -1013,9 +1213,14 @@ import_testdir() { mkdir -p "$TESTDIR" fi - echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE + cat >$STATEFILE</dev/null && \ + [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then + dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true" + exit 1 + fi + + if [ -e "$IMAGE_PRIVATE" ]; then + echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)" + mount_initdir + else + if [ ! -e "$IMAGE_PUBLIC" ]; then + # Create the backing public image, but then completely unmount + # it and drop the loopback device responsible for it, since we're + # going to symlink/copy the image and mount it again from + # elsewhere. + test_create_image + test_setup_cleanup + umount_loopback + cleanup_loopdev + fi + + echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)" + if [ ${TEST_PARALLELIZE} -ne 0 ]; then + cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE" + else + ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE" + fi + + mount_initdir + fi + + setup_nspawn_root +} + test_run() { + mount_initdir + if [ -z "$TEST_NO_QEMU" ]; then - if run_qemu; then - check_result_qemu || return 1 + if run_qemu "$1"; then + check_result_qemu || { echo "QEMU test failed"; return 1; } else dwarn "can't run QEMU, skipping" fi fi if [ -z "$TEST_NO_NSPAWN" ]; then - if run_nspawn "nspawn-root"; then - check_result_nspawn "nspawn-root" || return 1 + mount_initdir + if run_nspawn "$initdir" "$1"; then + check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; } else dwarn "can't run systemd-nspawn, skipping" fi if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then - if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then - check_result_nspawn "unprivileged-nspawn-root" || return 1 + dir="$TESTDIR/unprivileged-nspawn-root" + if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "$dir" "$1"; then + check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; } else dwarn "can't run systemd-nspawn, skipping" fi @@ -1830,34 +2085,40 @@ do_test() { import_testdir import_initdir + testname="$(basename $PWD)" + while (($# > 0)); do case $1 in --run) - echo "TEST RUN: $TEST_DESCRIPTION" - test_run + echo "${testname} RUN: $TEST_DESCRIPTION" + test_run "$2" ret=$? if (( $ret == 0 )); then - echo "TEST RUN: $TEST_DESCRIPTION [OK]" + echo "${testname} RUN: $TEST_DESCRIPTION [OK]" else - echo "TEST RUN: $TEST_DESCRIPTION [FAILED]" + echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]" fi exit $ret;; --setup) - echo "TEST SETUP: $TEST_DESCRIPTION" + echo "${testname} SETUP: $TEST_DESCRIPTION" test_setup test_setup_cleanup ;; --clean) - echo "TEST CLEANUP: $TEST_DESCRIPTION" + echo "${testname} CLEANUP: $TEST_DESCRIPTION" test_cleanup ;; + --clean-again) + echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION" + test_cleanup_again + ;; --all) ret=0 - echo -n "TEST: $TEST_DESCRIPTION " + echo -n "${testname}: $TEST_DESCRIPTION " ( test_setup test_setup_cleanup - test_run + test_run "$2" ) "$TESTLOG" 2>&1 || ret=$? test_cleanup if [ $ret -eq 0 ]; then diff --git a/test/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh new file mode 100755 index 000000000..d0d083451 --- /dev/null +++ b/test/test-network-generator-conversion.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -ex + +if [[ -n "$1" ]]; then + generator=$1 +elif [[ -x /usr/lib/systemd/systemd-network-generator ]]; then + generator=/usr/lib/systemd/systemd-network-generator +elif [[ -x /lib/systemd/systemd-network-generator ]]; then + generator=/lib/systemd/systemd-network-generator +else + exit 1 +fi + +src="$(dirname "$0")/testdata/test-network-generator-conversion" + +for f in "$src"/test-*.input; do + echo "*** Running $f" + + ( + out=$(mktemp --directory) + trap "rm -rf '$out'" EXIT INT QUIT PIPE + + $generator --root "$out" -- $(cat $f) + + if ! diff -u "$out"/run/systemd/network ${f%.input}.expected; then + echo "**** Unexpected output for $f" + exit 1 + fi + ) || exit 1 +done diff --git a/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.expected/91-default.network b/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.expected/91-default.network rename to test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network diff --git a/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.input b/test/test-network-generator-conversion/test-01-dhcp.input similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.input rename to test/test-network-generator-conversion/test-01-dhcp.input diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.netdev b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.netdev rename to test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.network rename to test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth0.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth0.network rename to test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth1.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth1.network rename to test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.input b/test/test-network-generator-conversion/test-02-bridge.input similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-02-bridge.input rename to test/test-network-generator-conversion/test-02-bridge.input diff --git a/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.expected/90-enp3s0.network b/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.expected/90-enp3s0.network rename to test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network diff --git a/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.input b/test/test-network-generator-conversion/test-03-issue-14319.input similarity index 100% rename from test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.input rename to test/test-network-generator-conversion/test-03-issue-14319.input diff --git a/test/test-network/conf/23-bond199.network b/test/test-network/conf/23-bond199.network index 31e5d12f7..cad651156 100644 --- a/test/test-network/conf/23-bond199.network +++ b/test/test-network/conf/23-bond199.network @@ -1,2 +1,5 @@ [Match] Name=bond199 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/23-emit-lldp.network b/test/test-network/conf/23-emit-lldp.network index de3504538..9edaf871b 100644 --- a/test/test-network/conf/23-emit-lldp.network +++ b/test/test-network/conf/23-emit-lldp.network @@ -2,4 +2,5 @@ Name=veth-peer [Network] +IPv6AcceptRA=no EmitLLDP=yes diff --git a/test/test-network/conf/24-keep-configuration-static.network b/test/test-network/conf/24-keep-configuration-static.network index 0c65a1d9e..7602927d8 100644 --- a/test/test-network/conf/24-keep-configuration-static.network +++ b/test/test-network/conf/24-keep-configuration-static.network @@ -2,4 +2,5 @@ Name=dummy98 [Network] +IPv6AcceptRA=no KeepConfiguration=static diff --git a/test/test-network/conf/24-lldp.network b/test/test-network/conf/24-lldp.network index fbdfb1b67..84723138f 100644 --- a/test/test-network/conf/24-lldp.network +++ b/test/test-network/conf/24-lldp.network @@ -2,4 +2,5 @@ Name=veth99 [Network] +IPv6AcceptRA=no LLDP=yes diff --git a/test/test-network/conf/24-search-domain.network b/test/test-network/conf/24-search-domain.network index 124af438a..5c37d2f61 100644 --- a/test/test-network/conf/24-search-domain.network +++ b/test/test-network/conf/24-search-domain.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Address=192.168.42.100/24 DNS=192.168.42.1 Domains= one two three four five six seven eight nine ten diff --git a/test/test-network/conf/25-address-link-section.network b/test/test-network/conf/25-address-link-section.network index 759e83c32..d6ab34027 100644 --- a/test/test-network/conf/25-address-link-section.network +++ b/test/test-network/conf/25-address-link-section.network @@ -3,3 +3,6 @@ Name=dummy98 [Link] MACAddress=00:01:02:aa:bb:cc + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-address-preferred-lifetime-zero.network b/test/test-network/conf/25-address-preferred-lifetime-zero.network index a1e760510..d3d02d282 100644 --- a/test/test-network/conf/25-address-preferred-lifetime-zero.network +++ b/test/test-network/conf/25-address-preferred-lifetime-zero.network @@ -2,6 +2,8 @@ Name=dummy98 [Network] +IPv6AcceptRA=no + # these lines are ignored Address=hogehoge Address=foofoo diff --git a/test/test-network/conf/25-fibrule-invert.network b/test/test-network/conf/25-fibrule-invert.network index bcca0c27a..b8b368fe5 100644 --- a/test/test-network/conf/25-fibrule-invert.network +++ b/test/test-network/conf/25-fibrule-invert.network @@ -1,6 +1,9 @@ [Match] Name=test1 +[Network] +IPv6AcceptRA=no + [RoutingPolicyRule] TypeOfService=0x08 Table=7 diff --git a/test/test-network/conf/25-fibrule-port-range.network b/test/test-network/conf/25-fibrule-port-range.network index 36646ec0f..77874b344 100644 --- a/test/test-network/conf/25-fibrule-port-range.network +++ b/test/test-network/conf/25-fibrule-port-range.network @@ -1,6 +1,9 @@ [Match] Name=test1 +[Network] +IPv6AcceptRA=no + [RoutingPolicyRule] TypeOfService=0x08 Table=7 diff --git a/test/test-network/conf/25-fibrule-uidrange.network b/test/test-network/conf/25-fibrule-uidrange.network index f42dfee32..44716e396 100644 --- a/test/test-network/conf/25-fibrule-uidrange.network +++ b/test/test-network/conf/25-fibrule-uidrange.network @@ -1,6 +1,9 @@ [Match] Name=test1 +[Network] +IPv6AcceptRA=no + [RoutingPolicyRule] TypeOfService=0x08 Table=7 diff --git a/test/test-network/conf/25-gateway-next-static.network b/test/test-network/conf/25-gateway-next-static.network index dfac8f48c..908e58881 100644 --- a/test/test-network/conf/25-gateway-next-static.network +++ b/test/test-network/conf/25-gateway-next-static.network @@ -2,5 +2,6 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Address=149.10.124.58/28 Gateway=149.10.124.60 diff --git a/test/test-network/conf/25-gateway-static.network b/test/test-network/conf/25-gateway-static.network index 448a21f2b..1ea184fa2 100644 --- a/test/test-network/conf/25-gateway-static.network +++ b/test/test-network/conf/25-gateway-static.network @@ -2,5 +2,6 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Address=149.10.124.58/28 Gateway=149.10.124.59 diff --git a/test/test-network/conf/25-ipv6-address-label-section.network b/test/test-network/conf/25-ipv6-address-label-section.network index 945b7dcc4..0742ad584 100644 --- a/test/test-network/conf/25-ipv6-address-label-section.network +++ b/test/test-network/conf/25-ipv6-address-label-section.network @@ -1,6 +1,9 @@ [Match] Name=dummy98 +[Network] +IPv6AcceptRA=no + [IPv6AddressLabel] Label=4444 Prefix=2004:da8:1:0::/64 diff --git a/test/test-network/conf/25-netdevsim.netdev b/test/test-network/conf/25-netdevsim.netdev deleted file mode 100644 index 899f2d9f4..000000000 --- a/test/test-network/conf/25-netdevsim.netdev +++ /dev/null @@ -1,3 +0,0 @@ -[NetDev] -Kind=netdevsim -Name=netdevsim99 diff --git a/test/test-network/conf/25-prefix-route-with-vrf.network b/test/test-network/conf/25-prefix-route-with-vrf.network new file mode 100644 index 000000000..fdc1e11c5 --- /dev/null +++ b/test/test-network/conf/25-prefix-route-with-vrf.network @@ -0,0 +1,26 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +VRF=vrf99 +Address=fdde:11:22::1/128 +Address=fdde:11:33::1/64 +Address=10.20.22.1/32 +Address=10.20.33.1/24 + +[Address] +Address=fdde:11:44::1/128 +AddPrefixRoute=no + +[Address] +Address=fdde:11:55::1/64 +AddPrefixRoute=no + +[Address] +Address=10.20.44.1/32 +AddPrefixRoute=no + +[Address] +Address=10.20.55.1/24 +AddPrefixRoute=no diff --git a/test/test-network/conf/25-prefix-route-without-vrf.network b/test/test-network/conf/25-prefix-route-without-vrf.network new file mode 100644 index 000000000..9354b55d5 --- /dev/null +++ b/test/test-network/conf/25-prefix-route-without-vrf.network @@ -0,0 +1,25 @@ +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no +Address=fdde:12:22::1/128 +Address=fdde:12:33::1/64 +Address=10.21.22.1/32 +Address=10.21.33.1/24 + +[Address] +Address=fdde:12:44::1/128 +AddPrefixRoute=no + +[Address] +Address=fdde:12:55::1/64 +AddPrefixRoute=no + +[Address] +Address=10.21.44.1/32 +AddPrefixRoute=no + +[Address] +Address=10.21.55.1/24 +AddPrefixRoute=no diff --git a/test/test-network/conf/25-qdisc-cake.network b/test/test-network/conf/25-qdisc-cake.network new file mode 100644 index 000000000..a1b00f258 --- /dev/null +++ b/test/test-network/conf/25-qdisc-cake.network @@ -0,0 +1,12 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[CAKE] +Parent=root +Handle=3a +OverheadBytes=128 +Bandwidth=500M diff --git a/test/test-network/conf/25-qdisc-clsact-and-htb.network b/test/test-network/conf/25-qdisc-clsact-and-htb.network new file mode 100644 index 000000000..fd2520db5 --- /dev/null +++ b/test/test-network/conf/25-qdisc-clsact-and-htb.network @@ -0,0 +1,203 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[QDisc] +Parent=clsact + +[HierarchyTokenBucket] +Parent=root +Handle=0002 +DefaultClass=30 +RateToQuantum=20 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0030 +Priority=1 +QuantumBytes=4000 +MTUBytes=1700 +OverheadBytes=100 +Rate=1M +BufferBytes=123456 +CeilRate=0.5M +CeilBufferBytes=123457 + +[NetworkEmulator] +Parent=2:30 +Handle=0030 +DelaySec=50ms +DelayJitterSec=10ms +LossRate=20% +PacketLimit=100 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0031 +Priority=1 +Rate=1M +CeilRate=0.5M + +[TrivialLinkEqualizer] +Parent=2:31 +Handle=0031 +Id=1 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0032 +Priority=1 +Rate=1M +CeilRate=0.5M + +[FairQueueing] +Parent=2:32 +Handle=0032 +PacketLimit=1000 +FlowLimit=200 +QuantumBytes=1500 +InitialQuantumBytes=13000 +MaximumRate=1M +Buckets=512 +OrphanMask=511 +Pacing=yes +CEThresholdSec=100ms + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0033 +Priority=1 +Rate=1M +CeilRate=0.5M + +[ControlledDelay] +Parent=2:33 +Handle=0033 +PacketLimit=2000 +TargetSec=10ms +IntervalSec=50ms +ECN=yes +CEThresholdSec=100ms + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0034 +Priority=1 +Rate=1M +CeilRate=0.5M + +[FairQueueingControlledDelay] +Parent=2:34 +Handle=0034 +PacketLimit=20480 +MemoryLimitBytes=64M +Flows=2048 +TargetSec=10ms +IntervalSec=200ms +QuantumBytes=1400 +ECN=yes +CEThresholdSec=100ms + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0035 +Priority=1 +Rate=1M +CeilRate=0.5M + +[TokenBucketFilter] +Parent=2:35 +Handle=0035 +Rate=1G +BurstBytes=5000 +LatencySec=70msec +PeakRate=100G +MTUBytes=1000000 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0036 +Priority=1 +Rate=1M +CeilRate=0.5M + +[StochasticFairnessQueueing] +Parent=2:36 +Handle=0036 +PerturbPeriodSec=5sec + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0037 +Priority=1 +Rate=1M +CeilRate=0.5M + +[PFIFO] +Parent=2:37 +Handle=0037 +PacketLimit=100000 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0038 +Priority=1 +Rate=1M +CeilRate=0.5M + +[GenericRandomEarlyDetection] +Parent=2:38 +Handle=0038 +VirtualQueues=12 +DefaultVirtualQueue=10 +GenericRIO=yes + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:0039 +Priority=1 +Rate=1M +CeilRate=0.5M + +[StochasticFairBlue] +Parent=2:39 +Handle=0039 +PacketLimit=200000 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003a +Priority=1 +Rate=1M +CeilRate=0.5M + +[BFIFO] +Parent=2:3a +Handle=003a +LimitBytes=1000000 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003b +Priority=1 +Rate=1M +CeilRate=0.5M + +[PFIFOHeadDrop] +Parent=2:3b +Handle=003b +PacketLimit=1023 + +[HierarchyTokenBucketClass] +Parent=root +ClassId=0002:003c +Priority=1 +Rate=1M +CeilRate=0.5M + +[PFIFOFast] +Parent=2:3c +Handle=003c diff --git a/test/test-network/conf/25-qdisc-clsact-root-compat.network b/test/test-network/conf/25-qdisc-clsact-root-compat.network deleted file mode 100644 index 1f0dea46f..000000000 --- a/test/test-network/conf/25-qdisc-clsact-root-compat.network +++ /dev/null @@ -1,12 +0,0 @@ -[Match] -Name=dummy98 - -[Network] -IPv6AcceptRA=no -Address=10.1.2.3/16 - -#[TrafficControlQueueingDiscipline] -#Parent=root - -[TrafficControlQueueingDiscipline] -Parent=clsact diff --git a/test/test-network/conf/25-qdisc-drr.network b/test/test-network/conf/25-qdisc-drr.network new file mode 100644 index 000000000..dff8b0978 --- /dev/null +++ b/test/test-network/conf/25-qdisc-drr.network @@ -0,0 +1,15 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[DeficitRoundRobinScheduler] +Parent=root +Handle=0002 + +[DeficitRoundRobinSchedulerClass] +Parent=root +ClassId=0002:0030 +QuantumBytes=2000 diff --git a/test/test-network/conf/25-qdisc-ets.network b/test/test-network/conf/25-qdisc-ets.network new file mode 100644 index 000000000..a8c6b6823 --- /dev/null +++ b/test/test-network/conf/25-qdisc-ets.network @@ -0,0 +1,20 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[EnhancedTransmissionSelection] +Parent=root +Handle=3a +Bands=10 +StrictBands=3 +QuantumBytes=2 4 6 +QuantumBytes= +QuantumBytes=1 2 3 +QuantumBytes=4 5 +PriorityMap=8 7 6 5 +PriorityMap= +PriorityMap=3 4 5 +PriorityMap=6 7 diff --git a/test/test-network/conf/25-qdisc-fq-codel.network b/test/test-network/conf/25-qdisc-fq-codel.network deleted file mode 100644 index 42c8dfa7f..000000000 --- a/test/test-network/conf/25-qdisc-fq-codel.network +++ /dev/null @@ -1,27 +0,0 @@ -[Match] -Name=dummy98 - -[Network] -IPv6AcceptRA=no -Address=10.1.2.3/16 - -[FairQueueing] -Parent=root -Handle=0003 -PacketLimit=1000 -FlowLimit=200 -Quantum=1500 -InitialQuantum=13000 -MaximumRate=1M -Buckets=512 -OrphanMask=511 -Pacing=yes -CEThresholdSec=100ms - -[ControlledDelay] -Parent=clsact -PacketLimit=2000 -TargetSec=10ms -IntervalSec=50ms -ECN=yes -CEThresholdSec=100ms diff --git a/test/test-network/conf/25-qdisc-hhf.network b/test/test-network/conf/25-qdisc-hhf.network new file mode 100644 index 000000000..950536259 --- /dev/null +++ b/test/test-network/conf/25-qdisc-hhf.network @@ -0,0 +1,11 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 + +[HeavyHitterFilter] +Parent=root +Handle=3a +PacketLimit=1022 diff --git a/test/test-network/conf/25-qdisc-ingress-root.network b/test/test-network/conf/25-qdisc-ingress-root.network deleted file mode 100644 index f72b70114..000000000 --- a/test/test-network/conf/25-qdisc-ingress-root.network +++ /dev/null @@ -1,12 +0,0 @@ -[Match] -Name=test1 - -[Network] -IPv6AcceptRA=no -Address=10.1.2.4/16 - -#[QDisc] -#Parent=root - -[QDisc] -Parent=ingress diff --git a/test/test-network/conf/25-qdisc-netem-and-fqcodel.network b/test/test-network/conf/25-qdisc-netem-and-fqcodel.network deleted file mode 100644 index de03d0d88..000000000 --- a/test/test-network/conf/25-qdisc-netem-and-fqcodel.network +++ /dev/null @@ -1,25 +0,0 @@ -[Match] -Name=dummy98 - -[Network] -IPv6AcceptRA=no -Address=10.1.2.3/16 - -[NetworkEmulator] -Parent=root -Handle=001f -DelaySec=50ms -DelayJitterSec=10ms -LossRate=20% -PacketLimit=100 - -[FairQueueingControlledDelay] -Parent=ingress -PacketLimit=20480 -MemoryLimit=64M -Flows=2048 -TargetSec=10ms -IntervalSec=200ms -Quantum=1400 -ECN=yes -CEThresholdSec=100ms diff --git a/test/test-network/conf/25-qdisc-teql.network b/test/test-network/conf/25-qdisc-pie.network similarity index 66% rename from test/test-network/conf/25-qdisc-teql.network rename to test/test-network/conf/25-qdisc-pie.network index c8bb9034a..d41ceba3e 100644 --- a/test/test-network/conf/25-qdisc-teql.network +++ b/test/test-network/conf/25-qdisc-pie.network @@ -5,7 +5,7 @@ Name=dummy98 IPv6AcceptRA=no Address=10.1.2.3/16 -[TrivialLinkEqualizer] +[PIE] Parent=root -Handle=0002 -Id=1 +Handle=3a +PacketLimit=200000 diff --git a/test/test-network/conf/25-qdisc-qfq.network b/test/test-network/conf/25-qdisc-qfq.network new file mode 100644 index 000000000..3a24415c1 --- /dev/null +++ b/test/test-network/conf/25-qdisc-qfq.network @@ -0,0 +1,22 @@ +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.4/16 + +[QuickFairQueueing] +Parent=root +Handle=0002 + +[QuickFairQueueingClass] +Parent=root +ClassId=0002:0030 +Weight=2 +MaxPacketBytes=16000 + +[QuickFairQueueingClass] +Parent=root +ClassId=0002:0031 +Weight=10 +MaxPacketBytes=8000 diff --git a/test/test-network/conf/25-qdisc-tbf-and-sfq.network b/test/test-network/conf/25-qdisc-tbf-and-sfq.network deleted file mode 100644 index c960886b8..000000000 --- a/test/test-network/conf/25-qdisc-tbf-and-sfq.network +++ /dev/null @@ -1,19 +0,0 @@ -[Match] -Name=test1 - -[Network] -IPv6AcceptRA=no -Address=10.1.2.4/16 - -[TokenBucketFilter] -Parent=root -Handle=003f -Rate=1G -Burst=5K -LatencySec=70msec -PeakRate=100G -MTUBytes=1M - -[StochasticFairnessQueueing] -Parent=clsact -PerturbPeriodSec=5sec diff --git a/test/test-network/conf/25-route-vrf.network b/test/test-network/conf/25-route-vrf.network index e786066d3..038dff21a 100644 --- a/test/test-network/conf/25-route-vrf.network +++ b/test/test-network/conf/25-route-vrf.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no VRF=vrf99 Address=192.168.100.2/24 Gateway=192.168.100.1 diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network new file mode 100644 index 000000000..c962c3d84 --- /dev/null +++ b/test/test-network/conf/25-sriov.network @@ -0,0 +1,37 @@ +[Match] +Name=eni99np1 + +[Network] +Address=192.168.100.100/24 + +[SR-IOV] +VirtualFunction=0 +VLANId=5 +VLANProtocol=802.1ad +QualityOfService=1 +MACSpoofCheck=yes +QueryReceiveSideScaling=yes +Trust=yes +LinkState=yes +MACAddress=00:11:22:33:44:55 + +[SR-IOV] +VirtualFunction=1 +VLANId=6 +VLANProtocol=802.1Q +QualityOfService=2 +MACSpoofCheck=no +QueryReceiveSideScaling=no +Trust=no +LinkState=no +MACAddress=00:11:22:33:44:56 + +[SR-IOV] +VirtualFunction=2 +VLANId=7 +QualityOfService=3 +MACSpoofCheck=no +QueryReceiveSideScaling=no +Trust=no +LinkState=auto +MACAddress=00:11:22:33:44:57 diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network index 68be30547..dc1d6542c 100644 --- a/test/test-network/conf/25-sysctl.network +++ b/test/test-network/conf/25-sysctl.network @@ -9,3 +9,4 @@ IPv6HopLimit=5 IPv4ProxyARP=true IPv6ProxyNDP=true IPv6AcceptRA=no +IPv4AcceptLocal=yes diff --git a/test/test-network/conf/configure-without-carrier.network b/test/test-network/conf/25-test1.network similarity index 71% rename from test/test-network/conf/configure-without-carrier.network rename to test/test-network/conf/25-test1.network index 5bd9d7e84..965013f55 100644 --- a/test/test-network/conf/configure-without-carrier.network +++ b/test/test-network/conf/25-test1.network @@ -4,4 +4,3 @@ Name=test1 [Network] Address=192.168.0.15/24 Gateway=192.168.0.1 -ConfigureWithoutCarrier=true diff --git a/test/test-network/conf/25-test1.network.d/configure-without-carrier.conf b/test/test-network/conf/25-test1.network.d/configure-without-carrier.conf new file mode 100644 index 000000000..fed3b0eef --- /dev/null +++ b/test/test-network/conf/25-test1.network.d/configure-without-carrier.conf @@ -0,0 +1,2 @@ +[Network] +ConfigureWithoutCarrier=true diff --git a/test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf b/test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf new file mode 100644 index 000000000..b091e98fc --- /dev/null +++ b/test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf @@ -0,0 +1,2 @@ +[Network] +IgnoreCarrierLoss=false diff --git a/test/test-network/conf/25-vrf.network b/test/test-network/conf/25-vrf.network index 42ce5b192..d47ecf078 100644 --- a/test/test-network/conf/25-vrf.network +++ b/test/test-network/conf/25-vrf.network @@ -1,2 +1,5 @@ [Match] Name=vrf99 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-wireguard-no-peer.netdev b/test/test-network/conf/25-wireguard-no-peer.netdev new file mode 100644 index 000000000..13fd55d2f --- /dev/null +++ b/test/test-network/conf/25-wireguard-no-peer.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=wg97 +Kind=wireguard + +[WireGuard] +PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= +ListenPort=51821 +FwMark=1235 diff --git a/test/test-network/conf/25-wireguard-no-peer.network b/test/test-network/conf/25-wireguard-no-peer.network new file mode 100644 index 000000000..5c29d643f --- /dev/null +++ b/test/test-network/conf/25-wireguard-no-peer.network @@ -0,0 +1,2 @@ +[Match] +Name=wg97 diff --git a/test/test-network/conf/26-bridge-configure-without-carrier.network b/test/test-network/conf/26-bridge-configure-without-carrier.network new file mode 100644 index 000000000..e1196b866 --- /dev/null +++ b/test/test-network/conf/26-bridge-configure-without-carrier.network @@ -0,0 +1,9 @@ +[Match] +Name=bridge99 + +[Network] +LinkLocalAddressing=yes +IPv6AcceptRA=no +ConfigureWithoutCarrier=yes +Address=10.1.2.3/24 +Gateway=10.1.2.1 diff --git a/test/test-network/conf/6rd.network b/test/test-network/conf/6rd.network index 84e5af0ff..96bd561ff 100644 --- a/test/test-network/conf/6rd.network +++ b/test/test-network/conf/6rd.network @@ -2,4 +2,5 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=sittun99 diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network similarity index 72% rename from test/test-network/conf/dhcp-client-ipv4-use-routes-no.network rename to test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network index 21e21fabb..c980bf9fc 100644 --- a/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network @@ -4,6 +4,3 @@ Name=veth99 [Network] DHCP=ipv4 IPv6AcceptRA=false - -[DHCPv4] -UseRoutes=no diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf new file mode 100644 index 000000000..9c561fba1 --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf @@ -0,0 +1,2 @@ +[DHCPv4] +RoutesToDNS=no diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf new file mode 100644 index 000000000..2504c2056 --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf @@ -0,0 +1,2 @@ +[DHCPv4] +RoutesToDNS=yes diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf new file mode 100644 index 000000000..78d049301 --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf @@ -0,0 +1,2 @@ +[DHCPv4] +UseGateway=no diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf new file mode 100644 index 000000000..f6f597b0f --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf @@ -0,0 +1,2 @@ +[DHCPv4] +UseGateway=yes diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf new file mode 100644 index 000000000..38fa983cd --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf @@ -0,0 +1,2 @@ +[DHCPv4] +UseRoutes=no diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf new file mode 100644 index 000000000..3b2d0abf3 --- /dev/null +++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf @@ -0,0 +1,2 @@ +[DHCPv4] +UseRoutes=yes diff --git a/test/test-network/conf/erspan.network b/test/test-network/conf/erspan.network index 49364c506..d1a88cdaa 100644 --- a/test/test-network/conf/erspan.network +++ b/test/test-network/conf/erspan.network @@ -2,5 +2,6 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=erspan99 Tunnel=erspan98 diff --git a/test/test-network/conf/gretap.network b/test/test-network/conf/gretap.network index 1493fcf2b..1deaab45d 100644 --- a/test/test-network/conf/gretap.network +++ b/test/test-network/conf/gretap.network @@ -2,5 +2,6 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=gretap99 Tunnel=gretap98 diff --git a/test/test-network/conf/gretun.network b/test/test-network/conf/gretun.network index 5510b1c9b..68d95b180 100644 --- a/test/test-network/conf/gretun.network +++ b/test/test-network/conf/gretun.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=gretun99 Tunnel=gretun98 Tunnel=gretun97 diff --git a/test/test-network/conf/ip6gretap.network b/test/test-network/conf/ip6gretap.network index 7ae4e3aea..8434c62ef 100644 --- a/test/test-network/conf/ip6gretap.network +++ b/test/test-network/conf/ip6gretap.network @@ -2,5 +2,6 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=ip6gretap99 Tunnel=ip6gretap98 diff --git a/test/test-network/conf/ip6gretun.network b/test/test-network/conf/ip6gretun.network index 8fbee9885..ce2bbd879 100644 --- a/test/test-network/conf/ip6gretun.network +++ b/test/test-network/conf/ip6gretun.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=ip6gretun99 Tunnel=ip6gretun98 Tunnel=ip6gretun97 diff --git a/test/test-network/conf/ip6tnl.network b/test/test-network/conf/ip6tnl.network index 15c6d15d4..7ad1e1b5b 100644 --- a/test/test-network/conf/ip6tnl.network +++ b/test/test-network/conf/ip6tnl.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=ip6tnl99 Tunnel=ip6tnl98 Tunnel=ip6tnl97 diff --git a/test/test-network/conf/ipip.network b/test/test-network/conf/ipip.network index ea4b3a135..c1ef30519 100644 --- a/test/test-network/conf/ipip.network +++ b/test/test-network/conf/ipip.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=ipiptun99 Tunnel=ipiptun98 Tunnel=ipiptun97 diff --git a/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network b/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network index 49c6f31e7..18fecb880 100644 --- a/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network +++ b/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network @@ -4,4 +4,6 @@ Name=veth99 [Network] IPv6AcceptRA=true IPv6Token=::1a:2b:3c:4d +IPv6Token=::1a:2b:3c:4d +IPv6Token=::1a:2b:3c:4d IPv6Token=::fa:de:ca:fe diff --git a/test/test-network/conf/ipv6ra-prefix.network b/test/test-network/conf/ipv6ra-prefix.network index 7bb666136..9dc32cb4d 100644 --- a/test/test-network/conf/ipv6ra-prefix.network +++ b/test/test-network/conf/ipv6ra-prefix.network @@ -4,10 +4,13 @@ Name=veth99 [Network] DHCP=no IPv6PrefixDelegation=yes -Address=2001:db8:0:1::1/64 [IPv6Prefix] -Prefix=2001:db8:0:1::4/64 +Prefix=2001:db8:0:1::/64 + +[IPv6Prefix] +Prefix=2001:db8:0:2::/64 +Assign=yes [IPv6RoutePrefix] Route=2001:db0:fff::/64 diff --git a/test/test-network/conf/ipvlan.network b/test/test-network/conf/ipvlan.network index d05322055..b3fccb146 100644 --- a/test/test-network/conf/ipvlan.network +++ b/test/test-network/conf/ipvlan.network @@ -2,4 +2,5 @@ Name=test1 [Network] +IPv6AcceptRA=no IPVLAN=ipvlan99 diff --git a/test/test-network/conf/ipvtap.network b/test/test-network/conf/ipvtap.network index c81ba52e2..454916432 100644 --- a/test/test-network/conf/ipvtap.network +++ b/test/test-network/conf/ipvtap.network @@ -2,4 +2,5 @@ Name=test1 [Network] +IPv6AcceptRA=no IPVTAP=ipvtap99 diff --git a/test/test-network/conf/isatap.network b/test/test-network/conf/isatap.network index e8d03ed6b..945833085 100644 --- a/test/test-network/conf/isatap.network +++ b/test/test-network/conf/isatap.network @@ -2,4 +2,5 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=isataptun99 diff --git a/test/test-network/conf/macvlan.network b/test/test-network/conf/macvlan.network index a41c1f916..e18842ca2 100644 --- a/test/test-network/conf/macvlan.network +++ b/test/test-network/conf/macvlan.network @@ -2,4 +2,5 @@ Name=test1 [Network] +IPv6AcceptRA=no MACVLAN=macvlan99 diff --git a/test/test-network/conf/macvtap.network b/test/test-network/conf/macvtap.network index 6ee99ab9c..956d13fc3 100644 --- a/test/test-network/conf/macvtap.network +++ b/test/test-network/conf/macvtap.network @@ -2,4 +2,5 @@ Name=test1 [Network] +IPv6AcceptRA=no MACVTAP=macvtap99 diff --git a/test/test-network/conf/routing-policy-rule-dummy98.network b/test/test-network/conf/routing-policy-rule-dummy98.network index 8136c20ae..804597cec 100644 --- a/test/test-network/conf/routing-policy-rule-dummy98.network +++ b/test/test-network/conf/routing-policy-rule-dummy98.network @@ -1,6 +1,9 @@ [Match] Name=dummy98 +[Network] +IPv6AcceptRA=no + [RoutingPolicyRule] TypeOfService=0x08 Table=8 diff --git a/test/test-network/conf/routing-policy-rule-test1.network b/test/test-network/conf/routing-policy-rule-test1.network index ffcedc99a..3594602cb 100644 --- a/test/test-network/conf/routing-policy-rule-test1.network +++ b/test/test-network/conf/routing-policy-rule-test1.network @@ -1,6 +1,9 @@ [Match] Name=test1 +[Network] +IPv6AcceptRA=no + [RoutingPolicyRule] TypeOfService=0x08 Table=7 diff --git a/test/test-network/conf/sit.network b/test/test-network/conf/sit.network index 79909fcd6..571c5c93d 100644 --- a/test/test-network/conf/sit.network +++ b/test/test-network/conf/sit.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=sittun99 Tunnel=sittun98 Tunnel=sittun97 diff --git a/test/test-network/conf/state-file-tests.network b/test/test-network/conf/state-file-tests.network index d0233841f..1f7e7d16f 100644 --- a/test/test-network/conf/state-file-tests.network +++ b/test/test-network/conf/state-file-tests.network @@ -5,7 +5,8 @@ Name=dummy98 RequiredForOnline=routable [Network] -DNS=10.10.10.10 10.10.10.11 +IPv6AcceptRA=no +DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org Domains=hogehoge ~foofoo LLMNR=no diff --git a/test/test-network/conf/vti.network b/test/test-network/conf/vti.network index 761362e48..888e79a79 100644 --- a/test/test-network/conf/vti.network +++ b/test/test-network/conf/vti.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=vtitun99 Tunnel=vtitun98 Tunnel=vtitun97 diff --git a/test/test-network/conf/vti6.network b/test/test-network/conf/vti6.network index 60ccb77f5..0916de86d 100644 --- a/test/test-network/conf/vti6.network +++ b/test/test-network/conf/vti6.network @@ -2,6 +2,7 @@ Name=dummy98 [Network] +IPv6AcceptRA=no Tunnel=vti6tun99 Tunnel=vti6tun98 Tunnel=vti6tun97 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 97488243f..0ca1fb3bf 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3,6 +3,7 @@ # systemd-networkd tests import argparse +import itertools import os import re import shutil @@ -157,6 +158,81 @@ def expectedFailureIfAlternativeNameIsNotAvailable(): return f +def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable(): + def f(func): + call('rmmod netdevsim', stderr=subprocess.DEVNULL) + rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL) + if rc != 0: + return unittest.expectedFailure(func) + + try: + with open('/sys/bus/netdevsim/new_device', mode='w') as f: + f.write('99 1') + except Exception as error: + return unittest.expectedFailure(func) + + call('udevadm settle') + call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL) + try: + with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f: + f.write('3') + except Exception as error: + call('rmmod netdevsim', stderr=subprocess.DEVNULL) + return unittest.expectedFailure(func) + + call('rmmod netdevsim', stderr=subprocess.DEVNULL) + return func + + return f + +def expectedFailureIfCAKEIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root cake', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + +def expectedFailureIfPIEIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root pie', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + +def expectedFailureIfHHFIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root hhf', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + +def expectedFailureIfETSIsNotAvailable(): + def f(func): + call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL) + rc = call('tc qdisc add dev dummy98 parent root ets bands 10', stderr=subprocess.DEVNULL) + call('ip link del dummy98', stderr=subprocess.DEVNULL) + if rc == 0: + return func + else: + return unittest.expectedFailure(func) + + return f + def setUpModule(): global running_units @@ -408,6 +484,9 @@ class Utilities(): def check_link_exists(self, link): self.assertTrue(link_exists(link)) + def check_link_attr(self, *args): + self.assertEqual(read_link_attr(*args[:-1]), args[-1]); + def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True): """Wait for the link to reach the specified operstate and/or setup state. @@ -478,7 +557,7 @@ class Utilities(): if i > 0: time.sleep(1) output = check_output(f'ip {ipv} address show dev {link} scope {scope}') - if re.search(address_regex, output): + if re.search(address_regex, output) and 'tentative' not in output: break else: self.assertRegex(output, address_regex) @@ -706,6 +785,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'vtitun99', 'vxcan99', 'vxlan99', + 'wg97', 'wg98', 'wg99', ] @@ -792,6 +872,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vxlan.netdev', '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', + '25-wireguard-no-peer.netdev', + '25-wireguard-no-peer.network', '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', '25-wireguard.netdev', @@ -1075,9 +1157,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): def test_wireguard(self): copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network', '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', - '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt') + '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt', + '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network') start_networkd() - self.wait_online(['wg99:carrier', 'wg98:routable']) + self.wait_online(['wg99:carrier', 'wg98:routable', 'wg97:carrier']) if shutil.which('wg'): call('wg') @@ -1102,6 +1185,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): output = check_output('wg show wg98 private-key') self.assertRegex(output, r'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=') + output = check_output('wg show wg97 listen-port') + self.assertRegex(output, '51821') + output = check_output('wg show wg97 fwmark') + self.assertRegex(output, '0x4d3') + def test_geneve(self): copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network') start_networkd() @@ -1621,25 +1709,30 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '25-neighbor-ip-dummy.network', '25-neighbor-ip.network', '25-nexthop.network', - '25-qdisc-clsact-root-compat.network', - '25-qdisc-fq-codel.network', + '25-qdisc-cake.network', + '25-qdisc-clsact-and-htb.network', + '25-qdisc-drr.network', + '25-qdisc-ets.network', + '25-qdisc-hhf.network', '25-qdisc-ingress-netem-compat.network', - '25-qdisc-ingress-root.network', - '25-qdisc-netem-and-fqcodel.network', - '25-qdisc-tbf-and-sfq.network', - '25-qdisc-teql.network', + '25-qdisc-pie.network', + '25-qdisc-qfq.network', + '25-prefix-route-with-vrf.network', + '25-prefix-route-without-vrf.network', '25-route-ipv6-src.network', '25-route-static.network', '25-route-vrf.network', '25-gateway-static.network', '25-gateway-next-static.network', + '25-sriov.network', '25-sysctl-disable-ipv6.network', '25-sysctl.network', + '25-test1.network', '25-veth-peer.network', '25-veth.netdev', '25-vrf.netdev', + '25-vrf.network', '26-link-local-addressing-ipv6.network', - 'configure-without-carrier.network', 'routing-policy-rule-dummy98.network', 'routing-policy-rule-test1.network'] @@ -1725,16 +1818,128 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): print(output) self.assertNotRegex(output, '192.168.100.10/24') - def test_configure_without_carrier(self): - copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev') - start_networkd() - self.wait_online(['test1:routable']) + @expectedFailureIfModuleIsNotAvailable('vrf') + def test_prefix_route(self): + copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev', + '25-prefix-route-without-vrf.network', '11-dummy.netdev', + '25-vrf.netdev', '25-vrf.network') + for trial in range(2): + if trial == 0: + start_networkd() + else: + restart_networkd(3) - output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env) - print(output) - self.assertRegex(output, '192.168.0.15') - self.assertRegex(output, '192.168.0.1') - self.assertRegex(output, 'routable') + self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier']) + + output = check_output('ip route show table 42 dev dummy98') + print('### ip route show table 42 dev dummy98') + print(output) + self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1') + self.assertRegex(output, 'broadcast 10.20.33.0 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1') + self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1') + self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1') + self.assertRegex(output, 'broadcast 10.20.55.0 proto kernel scope link src 10.20.55.1') + self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1') + self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1') + output = check_output('ip -6 route show table 42 dev dummy98') + print('### ip -6 route show table 42 dev dummy98') + print(output) + if trial == 0: + # Kernel's bug? + self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium') + #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium') + self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'ff00::/8 metric 256 pref medium') + + print() + + output = check_output('ip route show dev test1') + print('### ip route show dev test1') + print(output) + self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1') + output = check_output('ip route show table local dev test1') + print('### ip route show table local dev test1') + print(output) + self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1') + self.assertRegex(output, 'broadcast 10.21.33.0 proto kernel scope link src 10.21.33.1') + self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1') + self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1') + self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1') + self.assertRegex(output, 'broadcast 10.21.55.0 proto kernel scope link src 10.21.55.1') + self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1') + self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1') + output = check_output('ip -6 route show dev test1') + print('### ip -6 route show dev test1') + print(output) + self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium') + self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium') + self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium') + output = check_output('ip -6 route show table local dev test1') + print('### ip -6 route show table local dev test1') + print(output) + self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium') + self.assertRegex(output, 'ff00::/8 metric 256 pref medium') + + def test_configure_without_carrier(self): + copy_unit_to_networkd_unit_path('11-dummy.netdev') + start_networkd() + self.wait_operstate('test1', 'off', '') + check_output('ip link set dev test1 up carrier off') + + copy_unit_to_networkd_unit_path('25-test1.network.d/configure-without-carrier.conf', dropins=False) + restart_networkd() + self.wait_online(['test1:no-carrier']) + + carrier_map = {'on': '1', 'off': '0'} + routable_map = {'on': 'routable', 'off': 'no-carrier'} + for carrier in ['off', 'on', 'off']: + with self.subTest(carrier=carrier): + if carrier_map[carrier] != read_link_attr('test1', 'carrier'): + check_output(f'ip link set dev test1 carrier {carrier}') + self.wait_online([f'test1:{routable_map[carrier]}']) + + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env) + print(output) + self.assertRegex(output, '192.168.0.15') + self.assertRegex(output, '192.168.0.1') + self.assertRegex(output, routable_map[carrier]) + + def test_configure_without_carrier_yes_ignore_carrier_loss_no(self): + copy_unit_to_networkd_unit_path('11-dummy.netdev') + start_networkd() + self.wait_operstate('test1', 'off', '') + check_output('ip link set dev test1 up carrier off') + + copy_unit_to_networkd_unit_path('25-test1.network') + restart_networkd() + self.wait_online(['test1:no-carrier']) + + carrier_map = {'on': '1', 'off': '0'} + routable_map = {'on': 'routable', 'off': 'no-carrier'} + for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]: + with self.subTest(carrier=carrier, have_config=have_config): + if carrier_map[carrier] != read_link_attr('test1', 'carrier'): + check_output(f'ip link set dev test1 carrier {carrier}') + self.wait_online([f'test1:{routable_map[carrier]}']) + + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env) + print(output) + if have_config: + self.assertRegex(output, '192.168.0.15') + self.assertRegex(output, '192.168.0.1') + else: + self.assertNotRegex(output, '192.168.0.15') + self.assertNotRegex(output, '192.168.0.1') + self.assertRegex(output, routable_map[carrier]) def test_routing_policy_rule(self): copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev') @@ -2113,6 +2318,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1') self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1') self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1') + self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'accept_local'), '1') def test_sysctl_disable_ipv6(self): copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev') @@ -2133,7 +2339,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet6 .* scope link') output = check_output('ip -4 route show dev dummy98') print(output) - self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4') + self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4') output = check_output('ip -6 route show dev dummy98') print(output) self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static') @@ -2156,7 +2362,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet6 .* scope link') output = check_output('ip -4 route show dev dummy98') print(output) - self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4') + self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4') output = check_output('ip -6 route show dev dummy98') print(output) self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static') @@ -2254,71 +2460,181 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, '192.168.5.1') def test_qdisc(self): - copy_unit_to_networkd_unit_path('25-qdisc-netem-and-fqcodel.network', '12-dummy.netdev', - '25-qdisc-tbf-and-sfq.network', '11-dummy.netdev') - start_networkd() - - self.wait_online(['dummy98:routable', 'test1:routable']) - - output = check_output('tc qdisc show dev dummy98') - print(output) - self.assertRegex(output, 'qdisc netem 1f:') - self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%') - self.assertRegex(output, 'qdisc fq_codel') - self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn') - output = check_output('tc qdisc show dev test1') - print(output) - self.assertRegex(output, 'qdisc tbf 3f:') - self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms') - self.assertRegex(output, 'qdisc sfq') - self.assertRegex(output, 'perturb 5sec') - - def test_qdisc2(self): - copy_unit_to_networkd_unit_path('25-qdisc-fq-codel.network', '12-dummy.netdev', - '25-qdisc-ingress-root.network', '11-dummy.netdev') - start_networkd() - - self.wait_online(['dummy98:routable', 'test1:routable']) - - output = check_output('tc qdisc show dev dummy98') - print(output) - self.assertRegex(output, 'qdisc fq 3:') - self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511') - self.assertRegex(output, 'quantum 1500') - self.assertRegex(output, 'initial_quantum 13000') - self.assertRegex(output, 'maxrate 1Mbit') - self.assertRegex(output, 'qdisc codel') - self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn') - output = check_output('tc qdisc show dev test1') - print(output) - self.assertRegex(output, 'qdisc ingress') - - def test_qdisc3(self): - copy_unit_to_networkd_unit_path('25-qdisc-clsact-root-compat.network', '12-dummy.netdev', + copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev', '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev') + check_output('modprobe sch_teql max_equalizers=2') start_networkd() self.wait_online(['dummy98:routable', 'test1:routable']) + output = check_output('tc qdisc show dev test1') + print(output) + self.assertRegex(output, 'qdisc netem') + self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%') + self.assertRegex(output, 'qdisc ingress') + output = check_output('tc qdisc show dev dummy98') print(output) self.assertRegex(output, 'qdisc clsact') - output = check_output('tc qdisc show dev test1') - print(output) - self.assertRegex(output, 'qdisc netem') - self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%') - self.assertRegex(output, 'qdisc ingress') - def test_qdisc4(self): - copy_unit_to_networkd_unit_path('25-qdisc-teql.network', '12-dummy.netdev') - check_output('modprobe sch_teql max_equalizers=2') + self.assertRegex(output, 'qdisc htb 2: root') + self.assertRegex(output, r'default (0x30|30)') + + self.assertRegex(output, 'qdisc netem 30: parent 2:30') + self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%') + self.assertRegex(output, 'qdisc fq_codel') + self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') + + self.assertRegex(output, 'qdisc teql1 31: parent 2:31') + + self.assertRegex(output, 'qdisc fq 32: parent 2:32') + self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511') + self.assertRegex(output, 'quantum 1500') + self.assertRegex(output, 'initial_quantum 13000') + self.assertRegex(output, 'maxrate 1Mbit') + + self.assertRegex(output, 'qdisc codel 33: parent 2:33') + self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn') + + self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34') + self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn') + + self.assertRegex(output, 'qdisc tbf 35: parent 2:35') + self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms') + + self.assertRegex(output, 'qdisc sfq 36: parent 2:36') + self.assertRegex(output, 'perturb 5sec') + + self.assertRegex(output, 'qdisc pfifo 37: parent 2:37') + self.assertRegex(output, 'limit 100000p') + + self.assertRegex(output, 'qdisc gred 38: parent 2:38') + self.assertRegex(output, 'vqs 12 default 10 grio') + + self.assertRegex(output, 'qdisc sfb 39: parent 2:39') + self.assertRegex(output, 'limit 200000') + + self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a') + self.assertRegex(output, 'limit 1000000') + + self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b') + self.assertRegex(output, 'limit 1023p') + + self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c') + + output = check_output('tc -d class show dev dummy98') + print(output) + self.assertRegex(output, 'class htb 2:30 root leaf 30:') + self.assertRegex(output, 'class htb 2:31 root leaf 31:') + self.assertRegex(output, 'class htb 2:32 root leaf 32:') + self.assertRegex(output, 'class htb 2:33 root leaf 33:') + self.assertRegex(output, 'class htb 2:34 root leaf 34:') + self.assertRegex(output, 'class htb 2:35 root leaf 35:') + self.assertRegex(output, 'class htb 2:36 root leaf 36:') + self.assertRegex(output, 'class htb 2:37 root leaf 37:') + self.assertRegex(output, 'class htb 2:38 root leaf 38:') + self.assertRegex(output, 'class htb 2:39 root leaf 39:') + self.assertRegex(output, 'class htb 2:3a root leaf 3a:') + self.assertRegex(output, 'class htb 2:3b root leaf 3b:') + self.assertRegex(output, 'class htb 2:3c root leaf 3c:') + self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit') + self.assertRegex(output, 'burst 123456') + self.assertRegex(output, 'cburst 123457') + + def test_qdisc2(self): + copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev', + '25-qdisc-qfq.network', '11-dummy.netdev') start_networkd() + self.wait_online(['dummy98:routable', 'test1:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc drr 2: root') + output = check_output('tc class show dev dummy98') + print(output) + self.assertRegex(output, 'class drr 2:30 root quantum 2000b') + + output = check_output('tc qdisc show dev test1') + print(output) + self.assertRegex(output, 'qdisc qfq 2: root') + output = check_output('tc class show dev test1') + print(output) + self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000') + self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000') + + @expectedFailureIfCAKEIsNotAvailable() + def test_qdisc_cake(self): + copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev') + start_networkd() self.wait_online(['dummy98:routable']) output = check_output('tc qdisc show dev dummy98') print(output) - self.assertRegex(output, 'qdisc teql1 2:') + self.assertRegex(output, 'qdisc cake 3a: root') + self.assertRegex(output, 'bandwidth 500Mbit') + self.assertRegex(output, 'overhead 128') + + @expectedFailureIfPIEIsNotAvailable() + def test_qdisc_pie(self): + copy_unit_to_networkd_unit_path('25-qdisc-pie.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc pie 3a: root') + self.assertRegex(output, 'limit 200000') + + @expectedFailureIfHHFIsNotAvailable() + def test_qdisc_hhf(self): + copy_unit_to_networkd_unit_path('25-qdisc-hhf.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc hhf 3a: root') + self.assertRegex(output, 'limit 1022p') + + @expectedFailureIfETSIsNotAvailable() + def test_qdisc_ets(self): + copy_unit_to_networkd_unit_path('25-qdisc-ets.network', '12-dummy.netdev') + start_networkd() + self.wait_online(['dummy98:routable']) + + output = check_output('tc qdisc show dev dummy98') + print(output) + self.assertRegex(output, 'qdisc ets 3a: root') + self.assertRegex(output, 'bands 10 strict 3') + self.assertRegex(output, 'quanta 1 2 3 4 5') + self.assertRegex(output, 'priomap 3 4 5 6 7') + + @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable() + def test_sriov(self): + call('rmmod netdevsim', stderr=subprocess.DEVNULL) + call('modprobe netdevsim', stderr=subprocess.DEVNULL) + with open('/sys/bus/netdevsim/new_device', mode='w') as f: + f.write('99 1') + + call('udevadm settle') + call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL) + with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f: + f.write('3') + + copy_unit_to_networkd_unit_path('25-sriov.network') + start_networkd() + self.wait_online(['eni99np1:routable']) + + output = check_output('ip link show dev eni99np1') + print(output) + self.assertRegex(output, + 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *' + 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *' + 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off' + ) + + call('rmmod netdevsim', stderr=subprocess.DEVNULL) class NetworkdStateFileTests(unittest.TestCase, Utilities): links = [ @@ -2350,7 +2666,9 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): path = os.path.join('/run/systemd/netif/links/', ifindex) self.assertTrue(os.path.exists(path)) - time.sleep(2) + + # make link state file updated + check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env) with open(path) as f: data = f.read() @@ -2359,7 +2677,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes') self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable') self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network') - self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11') + self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com') self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org') self.assertRegex(data, r'DOMAINS=hogehoge') self.assertRegex(data, r'ROUTE_DOMAINS=foofoo') @@ -2368,17 +2686,16 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertRegex(data, r'DNSSEC=no') self.assertRegex(data, r'ADDRESSES=192.168.(10.10|12.12)/24 192.168.(12.12|10.10)/24') - check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12', '10.10.10.13', env=env) + check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env) check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env) check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env) check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env) check_output(*resolvectl_cmd, 'dnssec', 'dummy98', 'yes', env=env) check_output(*timedatectl_cmd, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env=env) - time.sleep(2) with open(path) as f: data = f.read() - self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13') + self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333') self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org') self.assertRegex(data, r'DOMAINS=hogehogehoge') self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo') @@ -2387,11 +2704,10 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertRegex(data, r'DNSSEC=yes') check_output(*timedatectl_cmd, 'revert', 'dummy98', env=env) - time.sleep(2) with open(path) as f: data = f.read() - self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13') + self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333') self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org') self.assertRegex(data, r'DOMAINS=hogehogehoge') self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo') @@ -2400,11 +2716,10 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities): self.assertRegex(data, r'DNSSEC=yes') check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env) - time.sleep(2) with open(path) as f: data = f.read() - self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11') + self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com') self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org') self.assertRegex(data, r'DOMAINS=hogehoge') self.assertRegex(data, r'ROUTE_DOMAINS=foofoo') @@ -2512,6 +2827,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): '11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', + '26-bridge-configure-without-carrier.network', '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', '26-bridge-vlan-master.network', @@ -2626,6 +2942,54 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ff00::/8 table local metric 256 (linkdown )?pref medium') + def test_bridge_configure_without_carrier(self): + copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-configure-without-carrier.network', + '11-dummy.netdev') + start_networkd() + + # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations + for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']: + with self.subTest(test=test): + if test == 'no-slave': + # bridge has no slaves; it's up but *might* not have carrier + self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30) + # due to a bug in the kernel, newly-created bridges are brought up + # *with* carrier, unless they have had any setting changed; e.g. + # their mac set, priority set, etc. Then, they will lose carrier + # as soon as a (down) slave interface is added, and regain carrier + # again once the slave interface is brought up. + #self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'add-slave': + # add slave to bridge, but leave it down; bridge is definitely no-carrier + self.check_link_attr('test1', 'operstate', 'down') + check_output('ip link set dev test1 master bridge99') + self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None) + self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'slave-up': + # bring up slave, which will have carrier; bridge gains carrier + check_output('ip link set dev test1 up') + self.wait_online(['bridge99:routable']) + self.check_link_attr('bridge99', 'carrier', '1') + elif test == 'slave-no-carrier': + # drop slave carrier; bridge loses carrier + check_output('ip link set dev test1 carrier off') + self.wait_online(['bridge99:no-carrier:no-carrier']) + self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'slave-carrier': + # restore slave carrier; bridge gains carrier + check_output('ip link set dev test1 carrier on') + self.wait_online(['bridge99:routable']) + self.check_link_attr('bridge99', 'carrier', '1') + elif test == 'slave-down': + # bring down slave; bridge loses carrier + check_output('ip link set dev test1 down') + self.wait_online(['bridge99:no-carrier:no-carrier']) + self.check_link_attr('bridge99', 'carrier', '0') + + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env) + self.assertRegex(output, '10.1.2.3') + self.assertRegex(output, '10.1.2.1') + def test_bridge_ignore_carrier_loss(self): copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', @@ -2826,7 +3190,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): 'dhcp-client-ipv4-dhcp-settings.network', 'dhcp-client-ipv4-only-ipv6-disabled.network', 'dhcp-client-ipv4-only.network', - 'dhcp-client-ipv4-use-routes-no.network', + 'dhcp-client-ipv4-use-routes-use-gateway.network', 'dhcp-client-ipv6-only.network', 'dhcp-client-ipv6-rapid-commit.network', 'dhcp-client-keep-configuration-dhcp-on-stop.network', @@ -2841,7 +3205,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): 'dhcp-client-use-dns-no.network', 'dhcp-client-use-dns-yes.network', 'dhcp-client-use-domains.network', - 'dhcp-client-use-routes-no.network', 'dhcp-client-vrf.network', 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network', 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network', @@ -2850,7 +3213,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): 'dhcp-server-decline.network', 'dhcp-server-veth-peer.network', 'dhcp-v4-server-veth-peer.network', - 'dhcp-client-use-domains.network', 'static.network'] def setUp(self): @@ -2879,6 +3241,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, '2600::') self.assertNotRegex(output, '192.168.5') + output = check_output('ip addr show dev veth99') + print(output) + self.assertRegex(output, '2600::') + self.assertNotRegex(output, '192.168.5') + self.assertNotRegex(output, 'tentative') + # Confirm that ipv6 token is not set in the kernel output = check_output('ip token show dev veth99') print(output) @@ -2931,8 +3299,23 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024') self.assertRegex(output, r'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024') - def test_dhcp_client_ipv4_use_routes_no(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-use-routes-no.network') + def test_dhcp_client_ipv4_use_routes_gateway(self): + for (routes, gateway, dnsroutes) in itertools.product([True, False, None], repeat=3): + self.setUp() + with self.subTest(routes=routes, gateway=gateway, dnsroutes=dnsroutes): + self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dnsroutes) + self.tearDown() + + def _test_dhcp_client_ipv4_use_routes_gateway(self, routes, gateway, dnsroutes): + testunit = 'dhcp-client-ipv4-use-routes-use-gateway.network' + testunits = ['25-veth.netdev', 'dhcp-server-veth-peer.network', testunit] + if routes != None: + testunits.append(f'{testunit}.d/use-routes-{routes}.conf'); + if gateway != None: + testunits.append(f'{testunit}.d/use-gateway-{gateway}.conf'); + if dnsroutes != None: + testunits.append(f'{testunit}.d/use-dns-routes-{dnsroutes}.conf'); + copy_unit_to_networkd_unit_path(*testunits, dropins=False) start_networkd() self.wait_online(['veth-peer:carrier']) @@ -2941,9 +3324,31 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): output = check_output('ip route show dev veth99') print(output) - self.assertNotRegex(output, r'192.168.5.5') - self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024') - self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024') + + # UseRoutes= defaults to true + useroutes = routes in [True, None] + # UseGateway= defaults to useroutes + usegateway = useroutes if gateway == None else gateway + + # Check UseRoutes= + if useroutes: + self.assertRegex(output, r'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.181 metric 1024') + else: + self.assertNotRegex(output, r'192.168.5.5') + + # Check UseGateway= + if usegateway: + self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024') + else: + self.assertNotRegex(output, r'default via 192.168.5.1') + + # Check RoutesToDNS=, which defaults to false + if dnsroutes: + self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024') + self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024') + else: + self.assertNotRegex(output, r'192.168.5.6') + self.assertNotRegex(output, r'192.168.5.7') def test_dhcp_client_ipv4_ipv6(self): copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network', @@ -3591,10 +3996,15 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): start_networkd() self.wait_online(['veth99:routable', 'veth-peer:routable']) - output = check_output('ip', '-6', 'route', 'show', 'dev', 'veth-peer') + output = check_output('ip -6 route show dev veth-peer') print(output) self.assertRegex(output, '2001:db8:0:1::/64 proto ra') + output = check_output('ip addr show dev veth99') + print(output) + self.assertNotRegex(output, '2001:db8:0:1') + self.assertRegex(output, '2001:db8:0:2') + class NetworkdMTUTests(unittest.TestCase, Utilities): links = ['dummy98'] diff --git a/test/test-path/basic.target b/test/test-path/basic.target deleted file mode 120000 index a882b72cc..000000000 --- a/test/test-path/basic.target +++ /dev/null @@ -1 +0,0 @@ -../../units/basic.target \ No newline at end of file diff --git a/test/test-path/basic.target b/test/test-path/basic.target new file mode 100644 index 000000000..4f4429224 --- /dev/null +++ b/test/test-path/basic.target @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Basic System +Documentation=man:systemd.special(7) +Requires=sysinit.target +Wants=sockets.target timers.target paths.target slices.target +After=sysinit.target sockets.target paths.target slices.target tmp.mount + +# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in +# remote-fs.target by default, hence pull them in explicitly here. Note that we +# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as +# we support that unit being masked, and this should not be considered an error. +RequiresMountsFor=/var /var/tmp +Wants=tmp.mount diff --git a/test/test-path/path-changed.service b/test/test-path/path-changed.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-changed.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-changed.service b/test/test-path/path-changed.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-changed.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-directorynotempty.service b/test/test-path/path-directorynotempty.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-directorynotempty.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-directorynotempty.service b/test/test-path/path-directorynotempty.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-directorynotempty.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-exists.service b/test/test-path/path-exists.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-exists.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-exists.service b/test/test-path/path-exists.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-exists.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-existsglob.service b/test/test-path/path-existsglob.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-existsglob.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-existsglob.service b/test/test-path/path-existsglob.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-existsglob.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-makedirectory.service b/test/test-path/path-makedirectory.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-makedirectory.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-makedirectory.service b/test/test-path/path-makedirectory.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-makedirectory.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-modified.service b/test/test-path/path-modified.service deleted file mode 120000 index 8bdf17883..000000000 --- a/test/test-path/path-modified.service +++ /dev/null @@ -1 +0,0 @@ -path-service.service \ No newline at end of file diff --git a/test/test-path/path-modified.service b/test/test-path/path-modified.service new file mode 100644 index 000000000..fb465d76b --- /dev/null +++ b/test/test-path/path-modified.service @@ -0,0 +1,7 @@ +[Unit] +Description=Service Test for Path units + +[Service] +ExecStart=/bin/true +Type=simple +RemainAfterExit=true diff --git a/test/test-path/path-mycustomunit.service b/test/test-path/path-mycustomunit.service index 172ac0d0d..bcdafe4f3 100644 --- a/test/test-path/path-mycustomunit.service +++ b/test/test-path/path-mycustomunit.service @@ -1,6 +1,7 @@ [Unit] -Description=Service Test Path Unit= +Description=Service Test Path Unit [Service] ExecStart=/bin/true -Type=oneshot +Type=simple +RemainAfterExit=true diff --git a/test/test-path/paths.target b/test/test-path/paths.target deleted file mode 120000 index b402796cb..000000000 --- a/test/test-path/paths.target +++ /dev/null @@ -1 +0,0 @@ -../../units/paths.target \ No newline at end of file diff --git a/test/test-path/paths.target b/test/test-path/paths.target new file mode 100644 index 000000000..9b6ed1c13 --- /dev/null +++ b/test/test-path/paths.target @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Paths +Documentation=man:systemd.special(7) diff --git a/test/test-path/sysinit.target b/test/test-path/sysinit.target deleted file mode 120000 index 9d10e5b2e..000000000 --- a/test/test-path/sysinit.target +++ /dev/null @@ -1 +0,0 @@ -../../units/sysinit.target \ No newline at end of file diff --git a/test/test-path/sysinit.target b/test/test-path/sysinit.target new file mode 100644 index 000000000..b6c16a141 --- /dev/null +++ b/test/test-path/sysinit.target @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Initialization +Documentation=man:systemd.special(7) +Conflicts=emergency.service emergency.target +Wants=local-fs.target swap.target +After=local-fs.target swap.target emergency.service emergency.target diff --git a/test/test-resolve/com~20200417.pkts b/test/test-resolve/com~20200417.pkts new file mode 100644 index 000000000..7927a791d Binary files /dev/null and b/test/test-resolve/com~20200417.pkts differ diff --git a/test/test-resolve/google.com.pkts b/test/test-resolve/google.com~20160131.pkts similarity index 100% rename from test/test-resolve/google.com.pkts rename to test/test-resolve/google.com~20160131.pkts diff --git a/test/test-resolve/google.com~20200417.pkts b/test/test-resolve/google.com~20200417.pkts new file mode 100644 index 000000000..be944cdb6 Binary files /dev/null and b/test/test-resolve/google.com~20200417.pkts differ diff --git a/test/test-resolve/michigan.gov~20200417.pkts b/test/test-resolve/michigan.gov~20200417.pkts new file mode 100644 index 000000000..5db23aafa Binary files /dev/null and b/test/test-resolve/michigan.gov~20200417.pkts differ diff --git a/test/test-resolve/org~20200417.pkts b/test/test-resolve/org~20200417.pkts new file mode 100644 index 000000000..5fa4a1628 Binary files /dev/null and b/test/test-resolve/org~20200417.pkts differ diff --git a/test/test-resolve/vdwaa.nl~20200417.pkts b/test/test-resolve/vdwaa.nl~20200417.pkts new file mode 100644 index 000000000..296410108 Binary files /dev/null and b/test/test-resolve/vdwaa.nl~20200417.pkts differ diff --git a/test/testdata b/test/testdata new file mode 120000 index 000000000..945c9b46d --- /dev/null +++ b/test/testdata @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/test/testsuite-04.units/forever-print-hola.service b/test/testsuite-04.units/forever-print-hola.service new file mode 100644 index 000000000..a0dc09568 --- /dev/null +++ b/test/testsuite-04.units/forever-print-hola.service @@ -0,0 +1,6 @@ +[Unit] +Description=ForeverPrintHola service + +[Service] +Type=simple +ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done' diff --git a/test/testsuite-06.units/hola.service b/test/testsuite-06.units/hola.service new file mode 100644 index 000000000..5dc633206 --- /dev/null +++ b/test/testsuite-06.units/hola.service @@ -0,0 +1,6 @@ +[Service] +Type=oneshot +ExecStart=/bin/echo Start Hola +ExecReload=/bin/echo Reload Hola +ExecStop=/bin/echo Stop Hola +RemainAfterExit=yes diff --git a/test/testsuite-06.units/load-systemd-test-module.service b/test/testsuite-06.units/load-systemd-test-module.service new file mode 100644 index 000000000..323a76c68 --- /dev/null +++ b/test/testsuite-06.units/load-systemd-test-module.service @@ -0,0 +1,14 @@ +[Unit] +Description=Load systemd-test module +DefaultDependencies=no +Requires=local-fs.target +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target autorelabel.service +ConditionSecurity=selinux + +[Service] +ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && cd /systemd-test-module && make -f /usr/share/selinux/devel/Makefile load' +Type=oneshot +TimeoutSec=0 +RemainAfterExit=yes diff --git a/test/testsuite-08.units/-.mount b/test/testsuite-08.units/-.mount new file mode 100644 index 000000000..af4e21975 --- /dev/null +++ b/test/testsuite-08.units/-.mount @@ -0,0 +1,12 @@ +[Unit] +Before=local-fs.target + +[Mount] +What=/dev/sda1 +Where=/ +Type=ext4 +Options=errors=remount-ro,noatime + +[Install] +WantedBy=local-fs.target +Alias=root.mount diff --git a/test/testsuite-08.units/local-fs.target.wants/-.mount b/test/testsuite-08.units/local-fs.target.wants/-.mount new file mode 120000 index 000000000..5566fceaa --- /dev/null +++ b/test/testsuite-08.units/local-fs.target.wants/-.mount @@ -0,0 +1 @@ +../-.mount \ No newline at end of file diff --git a/test/testsuite-08.units/root.mount b/test/testsuite-08.units/root.mount new file mode 120000 index 000000000..fd8c47d1b --- /dev/null +++ b/test/testsuite-08.units/root.mount @@ -0,0 +1 @@ +-.mount \ No newline at end of file diff --git a/test/testsuite-08.units/systemd-remount-fs.service b/test/testsuite-08.units/systemd-remount-fs.service new file mode 100644 index 000000000..398d61274 --- /dev/null +++ b/test/testsuite-08.units/systemd-remount-fs.service @@ -0,0 +1,11 @@ +[Unit] +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-fsck-root.service +Before=local-fs-pre.target local-fs.target shutdown.target +Wants=local-fs-pre.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/systemctl reload / diff --git a/test/testsuite-10.units/test10.service b/test/testsuite-10.units/test10.service new file mode 100644 index 000000000..d0be786b0 --- /dev/null +++ b/test/testsuite-10.units/test10.service @@ -0,0 +1,6 @@ +[Unit] +Requires=test10.socket +ConditionPathExistsGlob=/tmp/nonexistent + +[Service] +ExecStart=true diff --git a/test/testsuite-10.units/test10.socket b/test/testsuite-10.units/test10.socket new file mode 100644 index 000000000..9cceebbb8 --- /dev/null +++ b/test/testsuite-10.units/test10.socket @@ -0,0 +1,2 @@ +[Socket] +ListenStream=/run/test.ctl diff --git a/test/testsuite-11.units/fail-on-restart.service b/test/testsuite-11.units/fail-on-restart.service new file mode 100644 index 000000000..9264f151f --- /dev/null +++ b/test/testsuite-11.units/fail-on-restart.service @@ -0,0 +1,9 @@ +[Unit] +Description=Fail on restart +StartLimitIntervalSec=1m +StartLimitBurst=3 + +[Service] +Type=simple +ExecStart=false +Restart=always diff --git a/test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh b/test/testsuite-16.units/extend-timeout.sh similarity index 88% rename from test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh rename to test/testsuite-16.units/extend-timeout.sh index 40bf046dc..ed1af8afe 100755 --- a/test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh +++ b/test/testsuite-16.units/extend-timeout.sh @@ -4,23 +4,15 @@ set -e set -o pipefail # sleep interval (seconds) -sleep_interval=1 +: ${sleep_interval:=1} # extend_timeout_interval second(s) -extend_timeout_interval=1 +: ${extend_timeout_interval:=1} # number of sleep_intervals before READY=1 -start_intervals=10 +: ${start_intervals:=10} # number of sleep_intervals before exiting -stop_intervals=10 +: ${stop_intervals:=10} # run intervals, number of sleep_intervals to run -run_intervals=7 -# service name -SERVICE=unknown - -while [ $# -gt 0 ]; -do - eval ${1%=*}=${1#*=} - shift -done +: ${run_intervals:=7} # We convert to usec extend_timeout_interval=$(( $extend_timeout_interval * 1000000 )) diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service b/test/testsuite-16.units/fail-runtime.service similarity index 66% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service rename to test/testsuite-16.units/fail-runtime.service index e0b9f6a70..baa655f2f 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service +++ b/test/testsuite-16.units/fail-runtime.service @@ -1,13 +1,12 @@ - [Unit] Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.) [Service] - # EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval) # seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds. Type=notify TimeoutStartSec=4 TimeoutStopSec=4 RuntimeMaxSec=10 -ExecStart=/extend_timeout_test_service.sh SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0 +Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service b/test/testsuite-16.units/fail-start.service similarity index 66% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service rename to test/testsuite-16.units/fail-start.service index c3fcf23dc..882900440 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service +++ b/test/testsuite-16.units/fail-start.service @@ -1,4 +1,3 @@ - [Unit] Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.) @@ -10,4 +9,5 @@ Type=notify TimeoutStartSec=10 TimeoutStopSec=4 RuntimeMaxSec=4 -ExecStart=/extend_timeout_test_service.sh SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0 +Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service b/test/testsuite-16.units/fail-stop.service similarity index 78% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service rename to test/testsuite-16.units/fail-stop.service index ce76d10db..cdea2a9a2 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service +++ b/test/testsuite-16.units/fail-stop.service @@ -1,16 +1,15 @@ - [Unit] Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.) [Service] - # EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval) # seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds. Type=notify TimeoutStartSec=4 TimeoutStopSec=10 RuntimeMaxSec=4 -ExecStart=/extend_timeout_test_service.sh SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2 +Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh # Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1 # This file makes the test assess.sh quicker by notifing it that this test has finished. ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated' diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service b/test/testsuite-16.units/success-all.service similarity index 68% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service rename to test/testsuite-16.units/success-all.service index 666f4229b..e2d7e607b 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service +++ b/test/testsuite-16.units/success-all.service @@ -1,4 +1,3 @@ - [Unit] Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services @@ -11,4 +10,5 @@ Type=notify TimeoutStartSec=4 TimeoutStopSec=4 RuntimeMaxSec=4 -ExecStart=/extend_timeout_test_service.sh SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3 +Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service b/test/testsuite-16.units/success-runtime.service similarity index 60% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service rename to test/testsuite-16.units/success-runtime.service index dc226f505..15283b73a 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service +++ b/test/testsuite-16.units/success-runtime.service @@ -1,4 +1,3 @@ - [Unit] Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec) @@ -10,4 +9,5 @@ Type=notify TimeoutStartSec=4 TimeoutStopSec=4 RuntimeMaxSec=8 -ExecStart=/extend_timeout_test_service.sh SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0 +Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service b/test/testsuite-16.units/success-start.service similarity index 61% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service rename to test/testsuite-16.units/success-start.service index 228eece73..cfdcc33cc 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service +++ b/test/testsuite-16.units/success-start.service @@ -1,13 +1,12 @@ - [Unit] Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec) [Service] - # EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds. # Therefore startup is 6 seconds and < TimeoutStartSec so still successful. Type=notify TimeoutStartSec=8 TimeoutStopSec=4 RuntimeMaxSec=4 -ExecStart=/extend_timeout_test_service.sh SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0 +Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service b/test/testsuite-16.units/success-stop.service similarity index 61% rename from test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service rename to test/testsuite-16.units/success-stop.service index b809397bf..c4600ace4 100644 --- a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service +++ b/test/testsuite-16.units/success-stop.service @@ -1,13 +1,12 @@ - [Unit] Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec) [Service] - # EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds. # Therefore stop time is 6 seconds and < TimeoutStopSec so still successful. Type=notify TimeoutStartSec=4 TimeoutStopSec=8 RuntimeMaxSec=4 -ExecStart=/extend_timeout_test_service.sh SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1 +Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1 +ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh diff --git a/test/testsuite-28.units/specifier-j-depends-wants.service b/test/testsuite-28.units/specifier-j-depends-wants.service new file mode 100644 index 000000000..f9c6abb49 --- /dev/null +++ b/test/testsuite-28.units/specifier-j-depends-wants.service @@ -0,0 +1,7 @@ +[Unit] +Description=Dependent service for percent-j specifier +After=testsuite-28-pre.service + +[Service] +Type=oneshot +ExecStart=touch /tmp/test-specifier-j-wants diff --git a/test/testsuite-28.units/specifier-j-wants.service b/test/testsuite-28.units/specifier-j-wants.service new file mode 100644 index 000000000..facf5577b --- /dev/null +++ b/test/testsuite-28.units/specifier-j-wants.service @@ -0,0 +1,10 @@ +[Unit] +Description=Wants with percent-j specifier +Wants=specifier-j-depends-%j.service +After=specifier-j-depends-%j.service +After=testsuite-28-pre.service + +[Service] +Type=oneshot +ExecStart=test -f /tmp/test-specifier-j-%j +ExecStart=sh -c 'echo OK > /testok' diff --git a/test/testsuite-28.units/testsuite-28-pre.service b/test/testsuite-28.units/testsuite-28-pre.service new file mode 100644 index 000000000..2b8ef9891 --- /dev/null +++ b/test/testsuite-28.units/testsuite-28-pre.service @@ -0,0 +1,3 @@ +[Service] +ExecStart=rm -f /failed /testok +Type=oneshot diff --git a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf b/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf new file mode 100644 index 000000000..d5ed27cf6 --- /dev/null +++ b/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf @@ -0,0 +1,2 @@ +[Service] +WatchdogSec=10min diff --git a/test/TEST-15-DROPIN/testsuite.service b/test/testsuite-52.units/testsuite-52.service old mode 100644 new mode 100755 similarity index 51% rename from test/TEST-15-DROPIN/testsuite.service rename to test/testsuite-52.units/testsuite-52.service index 4c9f65e2b..93f847f04 --- a/test/TEST-15-DROPIN/testsuite.service +++ b/test/testsuite-52.units/testsuite-52.service @@ -2,5 +2,5 @@ Description=Testsuite service [Service] -ExecStart=/test-dropin.sh +ExecStart=/usr/lib/systemd/tests/testdata/%N.units/%N.sh Type=oneshot diff --git a/test/testsuite-52.units/testsuite-52.sh b/test/testsuite-52.units/testsuite-52.sh new file mode 100755 index 000000000..9cccf1b6c --- /dev/null +++ b/test/testsuite-52.units/testsuite-52.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -ex +set -o pipefail + +if ! test -x /usr/lib/systemd/tests/testdata/units/test-honor-first-shutdown.sh ; then + echo "honor-first-shutdown script not found - FAIL" > /testok + exit 0 +fi + +systemd-analyze log-level debug +systemd-analyze log-target console + +systemctl enable test-honor-first-shutdown.service +systemctl start test-honor-first-shutdown.service + +echo OK > /testok + +exit 0 diff --git a/test/timers.target b/test/timers.target deleted file mode 120000 index 576d47fed..000000000 --- a/test/timers.target +++ /dev/null @@ -1 +0,0 @@ -../units/timers.target \ No newline at end of file diff --git a/test/a-conj.service b/test/units/a-conj.service similarity index 100% rename from test/a-conj.service rename to test/units/a-conj.service diff --git a/test/a.service b/test/units/a.service similarity index 100% rename from test/a.service rename to test/units/a.service diff --git a/test/units/autorelabel.service b/test/units/autorelabel.service new file mode 100644 index 000000000..cb3884937 --- /dev/null +++ b/test/units/autorelabel.service @@ -0,0 +1,18 @@ +[Unit] +Description=Relabel all filesystems +DefaultDependencies=no +Requires=local-fs.target +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target +ConditionSecurity=selinux +ConditionPathExists=|/.autorelabel + +[Service] +ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot' +Type=oneshot +TimeoutSec=0 +RemainAfterExit=yes + +[Install] +WantedBy=basic.target diff --git a/test/b.service b/test/units/b.service similarity index 100% rename from test/b.service rename to test/units/b.service diff --git a/test/units/basic.target b/test/units/basic.target new file mode 100644 index 000000000..4f4429224 --- /dev/null +++ b/test/units/basic.target @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Basic System +Documentation=man:systemd.special(7) +Requires=sysinit.target +Wants=sockets.target timers.target paths.target slices.target +After=sysinit.target sockets.target paths.target slices.target tmp.mount + +# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in +# remote-fs.target by default, hence pull them in explicitly here. Note that we +# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as +# we support that unit being masked, and this should not be considered an error. +RequiresMountsFor=/var /var/tmp +Wants=tmp.mount diff --git a/test/c.service b/test/units/c.service similarity index 100% rename from test/c.service rename to test/units/c.service diff --git a/test/d.service b/test/units/d.service similarity index 100% rename from test/d.service rename to test/units/d.service diff --git a/test/daughter.service b/test/units/daughter.service similarity index 100% rename from test/daughter.service rename to test/units/daughter.service diff --git a/test/dml-discard-empty.service b/test/units/dml-discard-empty.service similarity index 100% rename from test/dml-discard-empty.service rename to test/units/dml-discard-empty.service diff --git a/test/dml-discard-set-ml.service b/test/units/dml-discard-set-ml.service similarity index 100% rename from test/dml-discard-set-ml.service rename to test/units/dml-discard-set-ml.service diff --git a/test/dml-discard.slice b/test/units/dml-discard.slice similarity index 100% rename from test/dml-discard.slice rename to test/units/dml-discard.slice diff --git a/test/dml-override-empty.service b/test/units/dml-override-empty.service similarity index 100% rename from test/dml-override-empty.service rename to test/units/dml-override-empty.service diff --git a/test/dml-override.slice b/test/units/dml-override.slice similarity index 100% rename from test/dml-override.slice rename to test/units/dml-override.slice diff --git a/test/dml-passthrough-empty.service b/test/units/dml-passthrough-empty.service similarity index 100% rename from test/dml-passthrough-empty.service rename to test/units/dml-passthrough-empty.service diff --git a/test/dml-passthrough-set-dml.service b/test/units/dml-passthrough-set-dml.service similarity index 100% rename from test/dml-passthrough-set-dml.service rename to test/units/dml-passthrough-set-dml.service diff --git a/test/dml-passthrough-set-ml.service b/test/units/dml-passthrough-set-ml.service similarity index 100% rename from test/dml-passthrough-set-ml.service rename to test/units/dml-passthrough-set-ml.service diff --git a/test/dml-passthrough.slice b/test/units/dml-passthrough.slice similarity index 100% rename from test/dml-passthrough.slice rename to test/units/dml-passthrough.slice diff --git a/test/dml.slice b/test/units/dml.slice similarity index 100% rename from test/dml.slice rename to test/units/dml.slice diff --git a/test/e.service b/test/units/e.service similarity index 100% rename from test/e.service rename to test/units/e.service diff --git a/test/end.service b/test/units/end.service similarity index 89% rename from test/end.service rename to test/units/end.service index 6e1996fd0..e7ed75ef0 100644 --- a/test/end.service +++ b/test/units/end.service @@ -1,6 +1,6 @@ [Unit] Description=End the test -After=testsuite.service +After=testsuite.target OnFailure=poweroff.target OnFailureJobMode=replace-irreversibly diff --git a/test/f.service b/test/units/f.service similarity index 100% rename from test/f.service rename to test/units/f.service diff --git a/test/g.service b/test/units/g.service similarity index 100% rename from test/g.service rename to test/units/g.service diff --git a/test/grandchild.service b/test/units/grandchild.service similarity index 100% rename from test/grandchild.service rename to test/units/grandchild.service diff --git a/test/h.service b/test/units/h.service similarity index 100% rename from test/h.service rename to test/units/h.service diff --git a/test/hello-after-sleep.target b/test/units/hello-after-sleep.target similarity index 100% rename from test/hello-after-sleep.target rename to test/units/hello-after-sleep.target diff --git a/test/hello.service b/test/units/hello.service similarity index 100% rename from test/hello.service rename to test/units/hello.service diff --git a/test/i.service b/test/units/i.service similarity index 100% rename from test/i.service rename to test/units/i.service diff --git a/test/loopy.service b/test/units/loopy.service similarity index 100% rename from test/loopy.service rename to test/units/loopy.service diff --git a/test/loopy.service.d/compat.conf b/test/units/loopy.service.d/compat.conf similarity index 100% rename from test/loopy.service.d/compat.conf rename to test/units/loopy.service.d/compat.conf diff --git a/test/units/loopy2.service b/test/units/loopy2.service new file mode 100644 index 000000000..9eb645748 --- /dev/null +++ b/test/units/loopy2.service @@ -0,0 +1,2 @@ +[Service] +ExecStart=/bin/true diff --git a/test/loopy3.service b/test/units/loopy3.service similarity index 100% rename from test/loopy3.service rename to test/units/loopy3.service diff --git a/test/units/loopy4.service b/test/units/loopy4.service new file mode 100644 index 000000000..606e26b5d --- /dev/null +++ b/test/units/loopy4.service @@ -0,0 +1,5 @@ +[Service] +ExecStart=/bin/true + +[Unit] +Conflicts=loopy4.service diff --git a/test/nomem.slice b/test/units/nomem.slice similarity index 100% rename from test/nomem.slice rename to test/units/nomem.slice diff --git a/test/nomemleaf.service b/test/units/nomemleaf.service similarity index 100% rename from test/nomemleaf.service rename to test/units/nomemleaf.service diff --git a/test/parent-deep.slice b/test/units/parent-deep.slice similarity index 100% rename from test/parent-deep.slice rename to test/units/parent-deep.slice diff --git a/test/parent.slice b/test/units/parent.slice similarity index 100% rename from test/parent.slice rename to test/units/parent.slice diff --git a/test/sched_idle_bad.service b/test/units/sched_idle_bad.service similarity index 100% rename from test/sched_idle_bad.service rename to test/units/sched_idle_bad.service diff --git a/test/sched_idle_ok.service b/test/units/sched_idle_ok.service similarity index 100% rename from test/sched_idle_ok.service rename to test/units/sched_idle_ok.service diff --git a/test/sched_rr_bad.service b/test/units/sched_rr_bad.service similarity index 100% rename from test/sched_rr_bad.service rename to test/units/sched_rr_bad.service diff --git a/test/sched_rr_change.service b/test/units/sched_rr_change.service similarity index 100% rename from test/sched_rr_change.service rename to test/units/sched_rr_change.service diff --git a/test/sched_rr_ok.service b/test/units/sched_rr_ok.service similarity index 100% rename from test/sched_rr_ok.service rename to test/units/sched_rr_ok.service diff --git a/test/units/shutdown.target b/test/units/shutdown.target new file mode 100644 index 000000000..d48e6d649 --- /dev/null +++ b/test/units/shutdown.target @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Shutdown +Documentation=man:systemd.special(7) +DefaultDependencies=no +RefuseManualStart=yes diff --git a/test/sleep.service b/test/units/sleep.service similarity index 100% rename from test/sleep.service rename to test/units/sleep.service diff --git a/test/units/sockets.target b/test/units/sockets.target new file mode 100644 index 000000000..9af67fdb1 --- /dev/null +++ b/test/units/sockets.target @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Sockets +Documentation=man:systemd.special(7) diff --git a/test/son.service b/test/units/son.service similarity index 100% rename from test/son.service rename to test/units/son.service diff --git a/test/units/sysinit.target b/test/units/sysinit.target new file mode 100644 index 000000000..b6c16a141 --- /dev/null +++ b/test/units/sysinit.target @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Initialization +Documentation=man:systemd.special(7) +Conflicts=emergency.service emergency.target +Wants=local-fs.target swap.target +After=local-fs.target swap.target emergency.service emergency.target diff --git a/test/units/test-honor-first-shutdown.service b/test/units/test-honor-first-shutdown.service new file mode 100644 index 000000000..3170f979e --- /dev/null +++ b/test/units/test-honor-first-shutdown.service @@ -0,0 +1,11 @@ +[Unit] +Description=Honor First Shutdown feature +After=multi-user.target + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStop=sh -c 'kill -KILL $MAINPID' +FailureAction=reboot + +[Install] +WantedBy=multi-user.target diff --git a/test/units/test-honor-first-shutdown.sh b/test/units/test-honor-first-shutdown.sh new file mode 100755 index 000000000..17c1ec968 --- /dev/null +++ b/test/units/test-honor-first-shutdown.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Honor first shutdown test script" +sleep infinity; diff --git a/test/units/testsuite-01.service b/test/units/testsuite-01.service new file mode 100644 index 000000000..85b9cf5a9 --- /dev/null +++ b/test/units/testsuite-01.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-01-BASIC +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-02.service b/test/units/testsuite-02.service new file mode 100644 index 000000000..13e7ec363 --- /dev/null +++ b/test/units/testsuite-02.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-02-CRYPTSETUP +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -x -e -c 'mountpoint /var; systemctl --state=failed --no-legend --no-pager >/failed; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-03.service b/test/units/testsuite-03.service new file mode 100644 index 000000000..fe18fdc7d --- /dev/null +++ b/test/units/testsuite-03.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-03-JOBS +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/units/testsuite-03.sh similarity index 100% rename from test/TEST-03-JOBS/test-jobs.sh rename to test/units/testsuite-03.sh diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service new file mode 100644 index 000000000..3d2b4a8bc --- /dev/null +++ b/test/units/testsuite-04.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-04-JOURNAL + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/units/testsuite-04.sh similarity index 78% rename from test/TEST-04-JOURNAL/test-journal.sh rename to test/units/testsuite-04.sh index 1431dad86..3dce73b77 100755 --- a/test/TEST-04-JOURNAL/test-journal.sh +++ b/test/units/testsuite-04.sh @@ -87,6 +87,18 @@ journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output grep -q "^_PID=$PID" /output grep -vq "^_PID=$PID" /output +# https://github.com/systemd/systemd/issues/15654 +ID=$(journalctl --new-id128 | sed -n 2p) +printf "This will\nusually fail\nand be truncated\n">/expected +systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;' +journalctl --sync +journalctl -b -o cat -t "$ID" >/output +cmp /expected /output +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]] + # Add new tests before here, the journald restarts below # may make tests flappy. @@ -106,4 +118,7 @@ systemctl kill --signal=SIGKILL systemd-journald sleep 3 [[ ! -f "/i-lose-my-logs" ]] +# https://github.com/systemd/systemd/issues/15528 +journalctl --follow --file=/var/log/journal/*/* | head -n1 || [[ $? -eq 1 ]] + touch /testok diff --git a/test/units/testsuite-05.service b/test/units/testsuite-05.service new file mode 100644 index 000000000..66356fd16 --- /dev/null +++ b/test/units/testsuite-05.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-05-RLIMITS + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-05.sh b/test/units/testsuite-05.sh new file mode 100755 index 000000000..9168e7279 --- /dev/null +++ b/test/units/testsuite-05.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +P=/run/systemd/system.conf.d +mkdir $P + +cat >$P/rlimits.conf </sys/fs/selinux/enforce +echo 1 >/sys/fs/selinux/enforce || { + echo "Can't make selinux enforcing, skipping test" + touch /testok + exit +} + runcon -t systemd_test_start_t systemctl start hola runcon -t systemd_test_reload_t systemctl reload hola runcon -t systemd_test_stop_t systemctl stop hola diff --git a/test/units/testsuite-07.service b/test/units/testsuite-07.service new file mode 100644 index 000000000..2506c211c --- /dev/null +++ b/test/units/testsuite-07.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-07-ISSUE-1981 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-07-ISSUE-1981/test-segfault.sh b/test/units/testsuite-07.sh similarity index 100% rename from test/TEST-07-ISSUE-1981/test-segfault.sh rename to test/units/testsuite-07.sh diff --git a/test/units/testsuite-08.service b/test/units/testsuite-08.service new file mode 100644 index 000000000..d961dc7ae --- /dev/null +++ b/test/units/testsuite-08.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-08-ISSUE-2730 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -x -c 'mount -o remount,rw /dev/sda1 && echo OK >/testok; systemctl poweroff' +Type=oneshot diff --git a/test/units/testsuite-09.service b/test/units/testsuite-09.service new file mode 100644 index 000000000..fc59e8088 --- /dev/null +++ b/test/units/testsuite-09.service @@ -0,0 +1,10 @@ +[Unit] +Description=TEST-09-ISSUE-2691 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=sh -c '>/testok' +ExecStop=sh -c 'kill -SEGV $$$$' +Type=oneshot +RemainAfterExit=yes +TimeoutStopSec=270s diff --git a/test/units/testsuite-10.service b/test/units/testsuite-10.service new file mode 100644 index 000000000..24f0da35a --- /dev/null +++ b/test/units/testsuite-10.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-10-ISSUE-2467 + +[Service] +ExecStartPre=rm -f /failed /testok +Type=oneshot +ExecStart=sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test10.socket; printf x >test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok' diff --git a/test/units/testsuite-11.service b/test/units/testsuite-11.service new file mode 100644 index 000000000..1544fd681 --- /dev/null +++ b/test/units/testsuite-11.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-11-ISSUE-3166 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-11.sh b/test/units/testsuite-11.sh new file mode 100755 index 000000000..708c7cebb --- /dev/null +++ b/test/units/testsuite-11.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -x + +systemctl start fail-on-restart.service +active_state=$(systemctl show --value --property ActiveState fail-on-restart.service) +while [[ "$active_state" == "activating" || "$active_state" == "active" ]]; do + sleep 1 + active_state=$(systemctl show --value --property ActiveState fail-on-restart.service) +done +systemctl is-failed fail-on-restart.service || exit 1 +touch /testok diff --git a/test/units/testsuite-12.service b/test/units/testsuite-12.service new file mode 100644 index 000000000..72894eff9 --- /dev/null +++ b/test/units/testsuite-12.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-12-ISSUE-3171 +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-12.sh b/test/units/testsuite-12.sh new file mode 100755 index 000000000..b5888a255 --- /dev/null +++ b/test/units/testsuite-12.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -x +set -e +set -o pipefail + +U=/run/systemd/system/test12.socket +cat <<'EOF' >$U +[Unit] +Description=Test 12 socket +[Socket] +Accept=yes +ListenStream=/run/test12.socket +SocketGroup=adm +SocketMode=0660 +EOF + +cat <<'EOF' > /run/systemd/system/test12@.service +[Unit] +Description=Test service +[Service] +StandardInput=socket +ExecStart=/bin/sh -x -c cat +EOF + +systemctl start test12.socket +systemctl is-active test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] +echo A | nc -w1 -U /run/test12.socket + +mv $U ${U}.disabled +systemctl daemon-reload +systemctl is-active test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] +echo B | nc -w1 -U /run/test12.socket && exit 1 + +mv ${U}.disabled $U +systemctl daemon-reload +systemctl is-active test12.socket +echo C | nc -w1 -U /run/test12.socket && exit 1 +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] + +systemctl restart test12.socket +systemctl is-active test12.socket +echo D | nc -w1 -U /run/test12.socket +[[ "$(stat --format='%G' /run/test12.socket)" == adm ]] + +touch /testok diff --git a/test/units/testsuite-13.service b/test/units/testsuite-13.service new file mode 100644 index 000000000..5086793a9 --- /dev/null +++ b/test/units/testsuite-13.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-13-NSPAWN-SMOKE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh new file mode 100755 index 000000000..b8fbf00ea --- /dev/null +++ b/test/units/testsuite-13.sh @@ -0,0 +1,183 @@ +#!/usr/bin/env bash +set -x +set -e +set -u +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +# check cgroup-v2 +is_v2_supported=no +mkdir -p /tmp/cgroup2 +if mount -t cgroup2 cgroup2 /tmp/cgroup2; then + is_v2_supported=yes + umount /tmp/cgroup2 +fi +rmdir /tmp/cgroup2 + +# check cgroup namespaces +is_cgns_supported=no +if [[ -f /proc/1/ns/cgroup ]]; then + is_cgns_supported=yes +fi + +is_user_ns_supported=no +# On some systems (e.g. CentOS 7) the default limit for user namespaces +# is set to 0, which causes the following unshare syscall to fail, even +# with enabled user namespaces support. By setting this value explicitly +# we can ensure the user namespaces support to be detected correctly. +sysctl -w user.max_user_namespaces=10000 +if unshare -U sh -c :; then + is_user_ns_supported=yes +fi + +function check_bind_tmp_path { + # https://github.com/systemd/systemd/issues/4789 + local _root="/var/lib/machines/testsuite-13.bind-tmp-path" + rm -rf "$_root" + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + >/tmp/bind + systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind' +} + +function check_norbind { + # https://github.com/systemd/systemd/issues/13170 + local _root="/var/lib/machines/testsuite-13.norbind-path" + rm -rf "$_root" + mkdir -p /tmp/binddir/subdir + echo -n "outer" > /tmp/binddir/subdir/file + mount -t tmpfs tmpfs /tmp/binddir/subdir + echo -n "inner" > /tmp/binddir/subdir/file + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi' +} + +function check_notification_socket { + # https://github.com/systemd/systemd/issues/4944 + local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/notify' + # /testsuite-13.nc-container is prepared by test.sh + systemd-nspawn --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd" + systemd-nspawn --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd" +} + +function check_os_release { + local _cmd='. /tmp/os-release +if [ -n "${ID:+set}" ] && [ "${ID}" != "${container_host_id}" ]; then exit 1; fi +if [ -n "${VERSION_ID:+set}" ] && [ "${VERSION_ID}" != "${container_host_version_id}" ]; then exit 1; fi +if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi +if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi +cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c +if echo test >> /run/host/os-release; then exit 1; fi +' + + local _os_release_source="/etc/os-release" + if [ ! -r "${_os_release_source}" ]; then + _os_release_source="/usr/lib/os-release" + elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then + # Ensure that /etc always wins if available + cp /usr/lib/os-release /etc + echo MARKER=1 >> /etc/os-release + fi + + systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd" + + if grep -q MARKER /etc/os-release; then + rm /etc/os-release + ln -s ../usr/lib/os-release /etc/os-release + fi +} + +function run { + if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then + printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2 + return 0 + fi + if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]]; then + printf "CGroup namespaces are not supported. Skipping.\n" >&2 + return 0 + fi + + local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3" + rm -rf "$_root" + /usr/lib/systemd/tests/testdata/create-busybox-container "$_root" + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then + [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1 + else + [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then + [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1 + else + [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1 + fi + + local _netns_opt="--network-namespace-path=/proc/self/ns/net" + + # --network-namespace-path and network-related options cannot be used together + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then + return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then + return 1 + fi + + # allow combination of --network-namespace-path and --private-network + if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then + return 1 + fi + + # test --network-namespace-path works with a network namespace created by "ip netns" + ip netns add nspawn_test + _netns_opt="--network-namespace-path=/run/netns/nspawn_test" + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP' + local r=$? + ip netns del nspawn_test + + if [ $r -ne 0 ]; then + return 1 + fi + + return 0 +} + +check_bind_tmp_path + +check_norbind + +check_notification_socket + +check_os_release + +for api_vfs_writable in yes no network; do + run no no $api_vfs_writable + run yes no $api_vfs_writable + run no yes $api_vfs_writable + run yes yes $api_vfs_writable +done + +touch /testok diff --git a/test/units/testsuite-14.service b/test/units/testsuite-14.service new file mode 100644 index 000000000..1606c68fb --- /dev/null +++ b/test/units/testsuite-14.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-14-MACHINE-ID + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStart=/bin/sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; echo OK >/testok' +Type=oneshot diff --git a/test/units/testsuite-14.sh b/test/units/testsuite-14.sh new file mode 100755 index 000000000..95ac9b65a --- /dev/null +++ b/test/units/testsuite-14.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -e +set -x + +function setup_root { + local _root="$1" + mkdir -p "$_root" + mount -t tmpfs tmpfs "$_root" + mkdir -p "$_root/etc" "$_root/run" +} + +function check { + printf "Expected\n" + cat "$1" + printf "\nGot\n" + cat "$2" + cmp "$1" "$2" +} + +r="$(pwd)/overwrite-broken-machine-id" +setup_root "$r" +systemd-machine-id-setup --print --root "$r" +echo abc >>"$r/etc/machine-id" +id=$(systemd-machine-id-setup --print --root "$r") +echo $id >expected +check expected "$r/etc/machine-id" + +r="$(pwd)/transient-machine-id" +setup_root "$r" +systemd-machine-id-setup --print --root "$r" +echo abc >>"$r/etc/machine-id" +mount -o remount,ro "$r" +mount -t tmpfs tmpfs "$r/run" +transient_id=$(systemd-machine-id-setup --print --root "$r") +mount -o remount,rw "$r" +commited_id=$(systemd-machine-id-setup --print --commit --root "$r") +[[ "$transient_id" = "$commited_id" ]] +check "$r/etc/machine-id" "$r/run/machine-id" diff --git a/test/units/testsuite-15.service b/test/units/testsuite-15.service new file mode 100644 index 000000000..09571ed1a --- /dev/null +++ b/test/units/testsuite-15.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-15-DROPIN + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-15-DROPIN/test-dropin.sh b/test/units/testsuite-15.sh similarity index 59% rename from test/TEST-15-DROPIN/test-dropin.sh rename to test/units/testsuite-15.sh index f80c9df10..b872a24c2 100755 --- a/test/TEST-15-DROPIN/test-dropin.sh +++ b/test/units/testsuite-15.sh @@ -7,6 +7,12 @@ _clear_service () { rm -f /{etc,run,usr/lib}/systemd/system/$1.service rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.d rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.{wants,requires} + if [[ $1 == *@ ]]; then + systemctl stop $1*.service 2>/dev/null || : + rm -f /{etc,run,usr/lib}/systemd/system/$1*.service + rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.d + rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.{wants,requires} + fi } clear_services () { @@ -56,65 +62,66 @@ test_basic_dropins () { echo "Testing basic dropins..." echo "*** test a wants b wants c" - create_services a b c - ln -s ../b.service /etc/systemd/system/a.service.wants/ - ln -s ../c.service /etc/systemd/system/b.service.wants/ - check_ok a Wants b.service - check_ok b Wants c.service + create_services test15-a test15-b test15-c + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/ + ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/ + check_ok test15-a Wants test15-b.service + check_ok test15-b Wants test15-c.service echo "*** test a wants,requires b" - create_services a b c - ln -s ../b.service /etc/systemd/system/a.service.wants/ - ln -s ../b.service /etc/systemd/system/a.service.requires/ - check_ok a Wants b.service - check_ok a Requires b.service + create_services test15-a test15-b test15-c + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/ + ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + check_ok test15-a Wants test15-b.service + check_ok test15-a Requires test15-b.service echo "*** test a wants nonexistent" - create_service a - ln -s ../nonexistent.service /etc/systemd/system/a.service.wants/ - check_ok a Wants nonexistent.service - systemctl start a - systemctl stop a + create_service test15-a + ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/ + check_ok test15-a Wants nonexistent.service + systemctl start test15-a + systemctl stop test15-a echo "*** test a requires nonexistent" - ln -sf ../nonexistent.service /etc/systemd/system/a.service.requires/ + ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/ systemctl daemon-reload - check_ok a Requires nonexistent.service + check_ok test15-a Requires nonexistent.service # 'b' is already loaded when 'c' pulls it in via a dropin. echo "*** test a,c require b" - create_services a b c - ln -sf ../b.service /etc/systemd/system/a.service.requires/ - ln -sf ../b.service /etc/systemd/system/c.service.requires/ - systemctl start a - check_ok c Requires b.service - systemctl stop a b + create_services test15-a test15-b test15-c + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/ + ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/ + systemctl start test15-a + check_ok test15-c Requires test15-b.service + systemctl stop test15-a test15-b # 'b' is already loaded when 'c' pulls it in via an alias dropin. echo "*** test a wants alias" - create_services a b c - ln -sf c.service /etc/systemd/system/c1.service - ln -sf ../c.service /etc/systemd/system/a.service.wants/ - ln -sf ../c1.service /etc/systemd/system/b.service.wants/ - systemctl start a - check_ok a Wants c.service - check_ok b Wants c.service - systemctl stop a c + create_services test15-a test15-b test15-c + ln -sf test15-c.service /etc/systemd/system/test15-c1.service + ln -sf ../test15-c.service /etc/systemd/system/test15-a.service.wants/ + ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/ + systemctl start test15-a + check_ok test15-a Wants test15-c.service + check_ok test15-b Wants test15-c.service + systemctl stop test15-a test15-c echo "*** test service.d/ top level drop-in" - create_services a b - check_ko a ExecCondition "/bin/echo a" - check_ko b ExecCondition "/bin/echo b" + create_services test15-a test15-b + check_ko test15-a ExecCondition "/bin/echo a" + check_ko test15-b ExecCondition "/bin/echo b" mkdir -p /usr/lib/systemd/system/service.d cat >/usr/lib/systemd/system/service.d/override.conf < /usr/lib/systemd/system/$dropin/override.conf + systemctl daemon-reload check_ok a-b-c ExecCondition "/bin/echo $dropin" done for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do @@ -294,153 +302,153 @@ test_alias_dropins () { echo "Testing alias dropins..." echo "*** test a wants b1 alias of b" - create_services a b - ln -sf b.service /etc/systemd/system/b1.service - ln -sf ../b1.service /etc/systemd/system/a.service.wants/ - check_ok a Wants b.service - systemctl start a - systemctl --quiet is-active b - systemctl stop a b - rm /etc/systemd/system/b1.service - clear_services a b + create_services test15-a test15-b + ln -sf test15-b.service /etc/systemd/system/test15-b1.service + ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/ + check_ok test15-a Wants test15-b.service + systemctl start test15-a + systemctl --quiet is-active test15-b + systemctl stop test15-a test15-b + rm /etc/systemd/system/test15-b1.service + clear_services test15-a test15-b # Check that dependencies don't vary. echo "*** test 2" - create_services a x y - mkdir -p /etc/systemd/system/a1.service.wants/ - ln -sf a.service /etc/systemd/system/a1.service - ln -sf ../x.service /etc/systemd/system/a.service.wants/ - ln -sf ../y.service /etc/systemd/system/a1.service.wants/ - check_ok a1 Wants x.service # see [1] - check_ok a1 Wants y.service - systemctl start a - check_ok a1 Wants x.service # see [2] - check_ok a1 Wants y.service - systemctl stop a x y - rm /etc/systemd/system/a1.service + create_services test15-a test15-x test15-y + mkdir -p /etc/systemd/system/test15-a1.service.wants/ + ln -sf test15-a.service /etc/systemd/system/test15-a1.service + ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/ + ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/ + check_ok test15-a1 Wants test15-x.service # see [1] + check_ok test15-a1 Wants test15-y.service + systemctl start test15-a + check_ok test15-a1 Wants test15-x.service # see [2] + check_ok test15-a1 Wants test15-y.service + systemctl stop test15-a test15-x test15-y + rm /etc/systemd/system/test15-a1.service - clear_services a x y + clear_services test15-a test15-x test15-y } test_masked_dropins () { echo "Testing masked dropins..." - create_services a b + create_services test15-a test15-b # 'b' is masked for both deps echo "*** test a wants,requires b is masked" - ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service - ln -sf /dev/null /etc/systemd/system/a.service.requires/b.service - check_ko a Wants b.service - check_ko a Requires b.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service + check_ko test15-a Wants test15-b.service + check_ko test15-a Requires test15-b.service # 'a' wants 'b' and 'b' is masked at a lower level echo "*** test a wants b, mask override" - ln -sf ../b.service /etc/systemd/system/a.service.wants/b.service - ln -sf /dev/null /usr/lib/systemd/system/a.service.wants/b.service - check_ok a Wants b.service + ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service + check_ok test15-a Wants test15-b.service # 'a' wants 'b' and 'b' is masked at a higher level echo "*** test a wants b, mask" - ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service - ln -sf ../b.service /usr/lib/systemd/system/a.service.wants/b.service - check_ko a Wants b.service + ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service + ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service + check_ko test15-a Wants test15-b.service # 'a' is masked but has an override config file echo "*** test a is masked but has an override" - create_services a b - ln -sf /dev/null /etc/systemd/system/a.service - cat >/usr/lib/systemd/system/a.service.d/override.conf </usr/lib/systemd/system/test15-a.service.d/override.conf </usr/lib/systemd/system/a.service.d/wants-b.conf</usr/lib/systemd/system/test15-a.service.d/wants-b.conf<> "${TL}" + journalctl -u ${service/_/-}.service >> "${TL}" fi } diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service new file mode 100644 index 000000000..ed2017a84 --- /dev/null +++ b/test/units/testsuite-17.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-17-UDEV-WANTS + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-17-UDEV-WANTS/testsuite.sh b/test/units/testsuite-17.sh similarity index 99% rename from test/TEST-17-UDEV-WANTS/testsuite.sh rename to test/units/testsuite-17.sh index 989c190ce..7f8740ba3 100755 --- a/test/TEST-17-UDEV-WANTS/testsuite.sh +++ b/test/units/testsuite-17.sh @@ -69,6 +69,6 @@ while : ; do sleep .5 done -echo OK > /testok +echo OK >/testok exit 0 diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service new file mode 100644 index 000000000..e4a945dc3 --- /dev/null +++ b/test/units/testsuite-18.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-18-FAILUREACTION + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-18-FAILUREACTION/testsuite.sh b/test/units/testsuite-18.sh similarity index 100% rename from test/TEST-18-FAILUREACTION/testsuite.sh rename to test/units/testsuite-18.sh diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service new file mode 100644 index 000000000..d6ad5bede --- /dev/null +++ b/test/units/testsuite-19.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-19-DELEGATE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-19-DELEGATE/testsuite.sh b/test/units/testsuite-19.sh similarity index 100% rename from test/TEST-19-DELEGATE/testsuite.sh rename to test/units/testsuite-19.sh diff --git a/test/units/testsuite-20.service b/test/units/testsuite-20.service new file mode 100644 index 000000000..d31d53117 --- /dev/null +++ b/test/units/testsuite-20.service @@ -0,0 +1,10 @@ +[Unit] +Description=TEST-20-MAINPIDGAMES +Before=getty-pre.target +Wants=getty-pre.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot +NotifyAccess=all diff --git a/test/TEST-20-MAINPIDGAMES/testsuite.sh b/test/units/testsuite-20.sh similarity index 58% rename from test/TEST-20-MAINPIDGAMES/testsuite.sh rename to test/units/testsuite-20.sh index f89402607..d94f6b2af 100755 --- a/test/TEST-20-MAINPIDGAMES/testsuite.sh +++ b/test/units/testsuite-20.sh @@ -5,7 +5,7 @@ set -o pipefail systemd-analyze log-level debug systemd-analyze log-target console -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Start a test process inside of our own cgroup sleep infinity & @@ -13,48 +13,48 @@ INTERNALPID=$! disown # Start a test process outside of our own cgroup -systemd-run -p DynamicUser=1 --unit=sleep.service /bin/sleep infinity -EXTERNALPID=`systemctl show -p MainPID --value sleep.service` +systemd-run -p DynamicUser=1 --unit=test20-sleep.service /bin/sleep infinity +EXTERNALPID=`systemctl show -P MainPID test20-sleep.service` # Update our own main PID to the external test PID, this should work systemd-notify MAINPID=$EXTERNALPID -test `systemctl show -p MainPID --value testsuite.service` -eq $EXTERNALPID +test `systemctl show -P MainPID testsuite-20.service` -eq $EXTERNALPID # Update our own main PID to the internal test PID, this should work, too systemd-notify MAINPID=$INTERNALPID -test `systemctl show -p MainPID --value testsuite.service` -eq $INTERNALPID +test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID # Update it back to our own PID, this should also work systemd-notify MAINPID=$$ -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Try to set it to PID 1, which it should ignore, because that's the manager systemd-notify MAINPID=1 -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Try to set it to PID 0, which is invalid and should be ignored systemd-notify MAINPID=0 -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Try to set it to a valid but non-existing PID, which should be ignored. (Note # that we set the PID to a value well above any known /proc/sys/kernel/pid_max, # which means we can be pretty sure it doesn't exist by coincidence) systemd-notify MAINPID=1073741824 -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges. systemd-notify --uid=1000 MAINPID=$EXTERNALPID -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ # Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges. systemd-notify --uid=1000 MAINPID=$INTERNALPID -test `systemctl show -p MainPID --value testsuite.service` -eq $INTERNALPID +test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID # Update it back to our own PID, this should also work systemd-notify --uid=1000 MAINPID=$$ -test `systemctl show -p MainPID --value testsuite.service` -eq $$ +test `systemctl show -P MainPID testsuite-20.service` -eq $$ -cat >/tmp/mainpid.sh </tmp/test20-mainpid.sh < /run/mainpidsh/pid EOF -chmod +x /tmp/mainpid.sh +chmod +x /tmp/test20-mainpid.sh -systemd-run --unit=mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/mainpid.sh -test `systemctl show -p MainPID --value mainpidsh.service` -eq `cat /run/mainpidsh/pid` +systemd-run --unit=test20-mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/test20-mainpid.sh +test `systemctl show -P MainPID test20-mainpidsh.service` -eq `cat /run/mainpidsh/pid` -cat >/tmp/mainpid2.sh </tmp/test20-mainpid2.sh < /run/mainpidsh2/pid chown 1001:1001 /run/mainpidsh2/pid EOF -chmod +x /tmp/mainpid2.sh +chmod +x /tmp/test20-mainpid2.sh -systemd-run --unit=mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/mainpid2.sh -test `systemctl show -p MainPID --value mainpidsh2.service` -eq `cat /run/mainpidsh2/pid` +systemd-run --unit=test20-mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/test20-mainpid2.sh +test `systemctl show -P MainPID test20-mainpidsh2.service` -eq `cat /run/mainpidsh2/pid` -cat >/dev/shm/mainpid3.sh </dev/shm/test20-mainpid3.sh </failed -for t in test-*.sh; do - echo "Running $t"; ./$t +for t in ${0%.sh}.*.sh; do + echo "Running $t"; ./$t done touch /testok diff --git a/test/units/testsuite-23.service b/test/units/testsuite-23.service new file mode 100644 index 000000000..b3b3297af --- /dev/null +++ b/test/units/testsuite-23.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-23-TYPE-EXEC + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-23-TYPE-EXEC/testsuite.sh b/test/units/testsuite-23.sh similarity index 100% rename from test/TEST-23-TYPE-EXEC/testsuite.sh rename to test/units/testsuite-23.sh diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service new file mode 100644 index 000000000..43d4816d4 --- /dev/null +++ b/test/units/testsuite-24.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-24-UNIT-TESTS + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-24-UNIT-TESTS/testsuite.sh b/test/units/testsuite-24.sh similarity index 90% rename from test/TEST-24-UNIT-TESTS/testsuite.sh rename to test/units/testsuite-24.sh index cc78adbbe..1ff1c3347 100755 --- a/test/TEST-24-UNIT-TESTS/testsuite.sh +++ b/test/units/testsuite-24.sh @@ -6,6 +6,9 @@ NPROC=$(nproc) MAX_QUEUE_SIZE=${NPROC:-2} IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*)) +# reset state +rm /failed-tests /skipped-tests /skipped + # Check & report test results # Arguments: # $1: test path @@ -21,23 +24,23 @@ function report_result() { if [[ $ret -ne 0 && $ret != 77 ]]; then echo "$name failed with $ret" - echo "$name" >> /failed-tests + echo "$name" >>/failed-tests { echo "--- $name begin ---" cat "/$name.log" echo "--- $name end ---" - } >> /failed + } >>/failed elif [[ $ret == 77 ]]; then echo "$name skipped" - echo "$name" >> /skipped-tests + echo "$name" >>/skipped-tests { echo "--- $name begin ---" cat "/$name.log" echo "--- $name end ---" - } >> /skipped + } >>/skipped else echo "$name OK" - echo "$name" >> /testok + echo "$name" >>/testok fi systemd-cat echo "--- $name ---" @@ -69,7 +72,7 @@ for task in "${TEST_LIST[@]}"; do if [[ -x $task ]]; then log_file="/${task##*/}.log" - $task &> "$log_file" & + $task &>"$log_file" & running[$task]=$! fi done diff --git a/test/units/testsuite-25.service b/test/units/testsuite-25.service new file mode 100644 index 000000000..45d8b6945 --- /dev/null +++ b/test/units/testsuite-25.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-25-IMPORT + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-25-IMPORT/testsuite.sh b/test/units/testsuite-25.sh similarity index 95% rename from test/TEST-25-IMPORT/testsuite.sh rename to test/units/testsuite-25.sh index 6dcb78050..e3dd43add 100755 --- a/test/TEST-25-IMPORT/testsuite.sh +++ b/test/units/testsuite-25.sh @@ -119,13 +119,13 @@ machinectl remove scratch4 ! test -f /var/lib/machines/scratch4 ! machinectl image-status scratch4 -# Test import-tar hypen/stdin pipe behavior +# Test import-tar hyphen/stdin pipe behavior cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5 test -d /var/lib/machines/scratch5 machinectl image-status scratch5 diff -r /var/tmp/scratch/ /var/lib/machines/scratch5 -# Test export-tar hypen/stdout pipe behavior +# Test export-tar hyphen/stdout pipe behavior mkdir -p /var/tmp/extract machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/ diff -r /var/tmp/scratch/ /var/tmp/extract/ @@ -133,6 +133,11 @@ rm -rf /var/tmp/extract rm -rf /var/tmp/scratch +# Test removal +machinectl remove scratch5 +! test -f /var/lib/machines/scratch5 +! machinectl image-status scratch5 + echo OK > /testok exit 0 diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service new file mode 100644 index 000000000..65b66835e --- /dev/null +++ b/test/units/testsuite-26.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-26-SETENV + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-26-SETENV/testsuite.sh b/test/units/testsuite-26.sh similarity index 100% rename from test/TEST-26-SETENV/testsuite.sh rename to test/units/testsuite-26.sh diff --git a/test/units/testsuite-27.service b/test/units/testsuite-27.service new file mode 100644 index 000000000..52185f057 --- /dev/null +++ b/test/units/testsuite-27.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-27-STDOUTFILE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-27-STDOUTFILE/testsuite.sh b/test/units/testsuite-27.sh similarity index 85% rename from test/TEST-27-STDOUTFILE/testsuite.sh rename to test/units/testsuite-27.sh index c522f75db..9d92e6e57 100755 --- a/test/TEST-27-STDOUTFILE/testsuite.sh +++ b/test/units/testsuite-27.sh @@ -5,7 +5,7 @@ set -o pipefail systemd-analyze log-level debug systemd-analyze log-target console -systemd-run --wait --unit=one \ +systemd-run --wait --unit=test27-one \ -p StandardOutput=file:/tmp/stdout \ -p StandardError=file:/tmp/stderr \ -p Type=exec \ @@ -17,7 +17,7 @@ cmp /tmp/stderr < /testok +echo OK >/testok exit 0 diff --git a/test/units/testsuite-28.service b/test/units/testsuite-28.service new file mode 100644 index 000000000..7ea863001 --- /dev/null +++ b/test/units/testsuite-28.service @@ -0,0 +1,11 @@ +[Unit] +Description=TEST-28-PERCENTJ-WANTEDBY +# Testsuite: Ensure %j Wants directives work +Wants=specifier-j-wants.service +After=specifier-j-wants.service +Requires=testsuite-28-pre.service +After=testsuite-28-pre.service + +[Service] +ExecStart=true +Type=oneshot diff --git a/test/units/testsuite-29.service b/test/units/testsuite-29.service new file mode 100644 index 000000000..90c2187bd --- /dev/null +++ b/test/units/testsuite-29.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-29-UDEV-ID_RENAMING + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-29-UDEV-ID_RENAMING/testsuite.sh b/test/units/testsuite-29.sh similarity index 100% rename from test/TEST-29-UDEV-ID_RENAMING/testsuite.sh rename to test/units/testsuite-29.sh diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service new file mode 100644 index 000000000..eb342f3d1 --- /dev/null +++ b/test/units/testsuite-30.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-30-ONCLOCKCHANGE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-30-ONCLOCKCHANGE/testsuite.sh b/test/units/testsuite-30.sh similarity index 100% rename from test/TEST-30-ONCLOCKCHANGE/testsuite.sh rename to test/units/testsuite-30.sh diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service new file mode 100644 index 000000000..07dfb0bb5 --- /dev/null +++ b/test/units/testsuite-31.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-31-DEVICE-ENUMERATION + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-31-DEVICE-ENUMERATION/testsuite.sh b/test/units/testsuite-31.sh similarity index 100% rename from test/TEST-31-DEVICE-ENUMERATION/testsuite.sh rename to test/units/testsuite-31.sh diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service new file mode 100644 index 000000000..aab95cb74 --- /dev/null +++ b/test/units/testsuite-32.service @@ -0,0 +1,8 @@ +[Unit] +Description=TEST-32-OOMPOLICY + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot +MemoryAccounting=yes diff --git a/test/TEST-32-OOMPOLICY/testsuite.sh b/test/units/testsuite-32.sh similarity index 62% rename from test/TEST-32-OOMPOLICY/testsuite.sh rename to test/units/testsuite-32.sh index aafafc118..c1704ab34 100755 --- a/test/TEST-32-OOMPOLICY/testsuite.sh +++ b/test/units/testsuite-32.sh @@ -8,30 +8,32 @@ set -o pipefail # an easier thing to test for, and also: let's not get confused by older # kernels where the concept was still new. -if test -f /sys/fs/cgroup/system.slice/testsuite.service/memory.oom.group ; then +if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then systemd-analyze log-level debug systemd-analyze log-target console # Run a service that is guaranteed to be the first candidate for OOM killing - systemd-run --unit=oomtest.service -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes /bin/sleep infinity + systemd-run --unit=oomtest.service \ + -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \ + sleep infinity # Trigger an OOM killer run - echo 1 > /proc/sys/kernel/sysrq - echo f > /proc/sysrq-trigger + echo 1 >/proc/sys/kernel/sysrq + echo f >/proc/sysrq-trigger while : ; do - STATE=`systemctl show -p ActiveState --value oomtest.service` + STATE=`systemctl show -P ActiveState oomtest.service` [ "$STATE" = "failed" ] && break sleep .5 done - RESULT=`systemctl show -p Result --value oomtest.service` + RESULT=`systemctl show -P Result oomtest.service` test "$RESULT" = "oom-kill" systemd-analyze log-level info fi -echo OK > /testok +echo OK >/testok exit 0 diff --git a/test/units/testsuite-33.service b/test/units/testsuite-33.service new file mode 100644 index 000000000..b64f1e0b7 --- /dev/null +++ b/test/units/testsuite-33.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-33-CLEAN-UNIT + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-33-CLEAN-UNIT/testsuite.sh b/test/units/testsuite-33.sh similarity index 100% rename from test/TEST-33-CLEAN-UNIT/testsuite.sh rename to test/units/testsuite-33.sh diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service new file mode 100644 index 000000000..361e32822 --- /dev/null +++ b/test/units/testsuite-34.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-34-DYNAMICUSERMIGRATE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh b/test/units/testsuite-34.sh similarity index 100% rename from test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh rename to test/units/testsuite-34.sh diff --git a/test/units/testsuite-36.service b/test/units/testsuite-36.service new file mode 100644 index 000000000..a681153ee --- /dev/null +++ b/test/units/testsuite-36.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-36-NUMAPOLICY + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/units/testsuite-36.sh similarity index 99% rename from test/TEST-36-NUMAPOLICY/testsuite.sh rename to test/units/testsuite-36.sh index b57b4a29a..aed938437 100755 --- a/test/TEST-36-NUMAPOLICY/testsuite.sh +++ b/test/units/testsuite-36.sh @@ -22,7 +22,7 @@ journalLog='journal.log' # Systemd config files testUnit='numa-test.service' -testUnitFile="/etc/systemd/system/$testUnit" +testUnitFile="/run/systemd/system/$testUnit" testUnitNUMAConf="$testUnitFile.d/numa.conf" # Sleep constants (we should probably figure out something better but nothing comes to mind) @@ -70,9 +70,9 @@ writePID1NUMAPolicy() { } writeTestUnit() { + mkdir -p $testUnitFile.d/ echo [Service] > $testUnitFile echo ExecStart=/bin/sleep 3600 >> $testUnitFile - mkdir -p $testUnitFile.d/ } writeTestUnitNUMAPolicy() { @@ -129,7 +129,7 @@ systemctlCheckNUMAProperties() { writeTestUnit # Create systemd config drop-in directory -confDir="/etc/systemd/system.conf.d/" +confDir="/run/systemd/system.conf.d/" mkdir -p "$confDir" if ! checkNUMA; then diff --git a/test/units/testsuite-37.service b/test/units/testsuite-37.service new file mode 100644 index 000000000..d25c6d2cf --- /dev/null +++ b/test/units/testsuite-37.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-37-RUNTIMEDIRECTORYPRESERVE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh b/test/units/testsuite-37.sh similarity index 100% rename from test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh rename to test/units/testsuite-37.sh diff --git a/test/units/testsuite-38-sleep.service b/test/units/testsuite-38-sleep.service new file mode 100644 index 000000000..859f97b36 --- /dev/null +++ b/test/units/testsuite-38-sleep.service @@ -0,0 +1,2 @@ +[Service] +ExecStart=/bin/sleep 3600 diff --git a/test/units/testsuite-38.service b/test/units/testsuite-38.service new file mode 100644 index 000000000..c848840ba --- /dev/null +++ b/test/units/testsuite-38.service @@ -0,0 +1,6 @@ +[Unit] +Description=TEST-38-FREEZER + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-38.sh b/test/units/testsuite-38.sh new file mode 100755 index 000000000..18b7bd6dc --- /dev/null +++ b/test/units/testsuite-38.sh @@ -0,0 +1,297 @@ +#!/usr/bin/env bash + +set -ex +set -o pipefail + +systemd-analyze log-level debug +systemd-analyze log-target console + +unit=testsuite-38-sleep.service + +start_test_service() { + systemctl daemon-reload + systemctl start "${unit}" +} + +dbus_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Freeze +} + +dbus_thaw() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Thaw +} + +dbus_freeze_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + FreezeUnit \ + s \ + "$1" +} + +dbus_thaw_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + ThawUnit \ + s \ + "$1" +} + +dbus_can_freeze() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + CanFreeze +} + +check_freezer_state() { + local suffix= + suffix="${1##*.}" + + local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" + local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" + + state=$(busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + FreezerState | cut -d " " -f2 | tr -d '"') + + [ "$state" = "$2" ] || { + echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 + exit 1 + } +} + +check_cgroup_state() { + grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events +} + +test_dbus_api() { + echo "Test that DBus API works:" + echo -n " - Freeze(): " + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - Thaw(): " + dbus_thaw "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - FreezeUnit(): " + dbus_freeze_unit "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - ThawUnit(): " + dbus_thaw_unit "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - CanFreeze(): " + output=$(dbus_can_freeze "${unit}") + [ "$output" = "b true" ] + echo "[ OK ]" + + echo +} + +test_jobs() { + local pid_before= + local pid_after= + echo "Test that it is possible to apply jobs on frozen units:" + + systemctl start "${unit}" + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + + echo -n " - restart: " + pid_before=$(systemctl show -p MainPID "${unit}" --value) + systemctl restart "${unit}" + pid_after=$(systemctl show -p MainPID "${unit}" --value) + [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" + + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + + echo -n " - stop: " + timeout 5s systemctl stop "${unit}" + echo "[ OK ]" + + echo +} + +test_systemctl() { + echo "Test that systemctl freeze/thaw verbs:" + + systemctl start "$unit" + + echo -n " - freeze: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + # Freezing already frozen unit should be NOP and return quickly + timeout 3s systemctl freeze "$unit" + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$unit" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + # Likewise thawing already running unit shouldn't block + timeout 3s systemctl thaw "$unit" + echo "[ OK ]" + + systemctl stop "$unit" + + echo +} + +test_systemctl_show() { + echo "Test systemctl show integration:" + + systemctl start "$unit" + + echo -n " - FreezerState property: " + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "running" ] + systemctl freeze "$unit" + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "frozen" ] + systemctl thaw "$unit" + echo "[ OK ]" + + echo -n " - CanFreeze property: " + state=$(systemctl show -p CanFreeze --value "$unit") + [ "$state" = "yes" ] + echo "[ OK ]" + + systemctl stop "$unit" + echo +} + +test_recursive() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test recursive freezing:" + + echo -n " - freeze: " + systemctl freeze "$slice" + check_freezer_state "${slice}" "frozen" + check_freezer_state "${unit}" "frozen" + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$slice" + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test_preserve_state() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" + + echo -n " - freeze from outside: " + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + # Give kernel some time to freeze the slice + sleep 1 + + # Our state should not be affected + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "running" + + # However actual kernel state should be frozen + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside: " + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + sleep 1 + + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events + echo "[ OK ]" + + echo -n " - thaw from outside while inner service is frozen: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze + check_freezer_state "${slice}" "running" + check_freezer_state "${unit}" "frozen" + echo "[ OK ]" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { + start_test_service + test_dbus_api + test_systemctl + test_systemctl_show + test_jobs + test_recursive + test_preserve_state +} + +echo OK > /testok +exit 0 diff --git a/test/units/testsuite-39.service b/test/units/testsuite-39.service new file mode 100644 index 000000000..395fe803e --- /dev/null +++ b/test/units/testsuite-39.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-39-EXECRELOAD + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-39-EXECRELOAD/testsuite.sh b/test/units/testsuite-39.sh old mode 100644 new mode 100755 similarity index 100% rename from test/TEST-39-EXECRELOAD/testsuite.sh rename to test/units/testsuite-39.sh diff --git a/test/units/testsuite-40.service b/test/units/testsuite-40.service new file mode 100644 index 000000000..38b0bd80d --- /dev/null +++ b/test/units/testsuite-40.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-40-EXEC-COMMAND-EX + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh b/test/units/testsuite-40.sh similarity index 100% rename from test/TEST-40-EXEC-COMMAND-EX/testsuite.sh rename to test/units/testsuite-40.sh diff --git a/test/units/testsuite-41.service b/test/units/testsuite-41.service new file mode 100644 index 000000000..766cb4c99 --- /dev/null +++ b/test/units/testsuite-41.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-41-ONESHOT-RESTART + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-41-ONESHOT-RESTART/testsuite.sh b/test/units/testsuite-41.sh similarity index 84% rename from test/TEST-41-ONESHOT-RESTART/testsuite.sh rename to test/units/testsuite-41.sh index 905f32e99..81fa1716f 100755 --- a/test/TEST-41-ONESHOT-RESTART/testsuite.sh +++ b/test/units/testsuite-41.sh @@ -12,16 +12,16 @@ systemd-analyze log-target console ! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1" for ((secs=0; secs<$MAX_SECS; secs++)); do - [[ "$(systemctl show one.service -p NRestarts --value)" -le 0 ]] || break + [[ "$(systemctl show one.service -P NRestarts)" -le 0 ]] || break sleep 1 done -if [[ "$(systemctl show one.service -p NRestarts --value)" -le 0 ]]; then +if [[ "$(systemctl show one.service -P NRestarts)" -le 0 ]]; then exit 1 fi -TMP_FILE="/test-41-oneshot-restart-test" +TMP_FILE="/tmp/test-41-oneshot-restart-test" -touch $TMP_FILE +: >$TMP_FILE # test two: make sure StartLimitBurst correctly limits the number of restarts # and restarts execution of the unit from the first ExecStart= diff --git a/test/units/testsuite-42.service b/test/units/testsuite-42.service new file mode 100644 index 000000000..a5504b515 --- /dev/null +++ b/test/units/testsuite-42.service @@ -0,0 +1,9 @@ +[Unit] +Description=TEST-42-EXECSTOPPOST +Before=getty-pre.target +Wants=getty-pre.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-42-EXECSTOPPOST/testsuite.sh b/test/units/testsuite-42.sh similarity index 100% rename from test/TEST-42-EXECSTOPPOST/testsuite.sh rename to test/units/testsuite-42.sh diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service new file mode 100644 index 000000000..31248f17e --- /dev/null +++ b/test/units/testsuite-43.service @@ -0,0 +1,9 @@ +[Unit] +Description=TEST-43-PRIVATEUSER-UNPRIV +After=systemd-logind.service user@4711.service +Wants=user@4711.service + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh b/test/units/testsuite-43.sh similarity index 74% rename from test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh rename to test/units/testsuite-43.sh index ff94ad4d8..ec84868a2 100755 --- a/test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh +++ b/test/units/testsuite-43.sh @@ -10,10 +10,10 @@ runas() { su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@" } -runas testuser systemd-run --user --unit=test-private-users \ +runas testuser systemd-run --wait --user --unit=test-private-users \ -p PrivateUsers=yes -P echo hello -runas testuser systemd-run --user --unit=test-private-tmp-innerfile \ +runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \ -p PrivateUsers=yes -p PrivateTmp=yes \ -P touch /tmp/innerfile.txt # File should not exist outside the job's tmp directory. @@ -21,17 +21,17 @@ test ! -e /tmp/innerfile.txt touch /tmp/outerfile.txt # File should not appear in unit's private tmp. -runas testuser systemd-run --user --unit=test-private-tmp-outerfile \ +runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \ -p PrivateUsers=yes -p PrivateTmp=yes \ -P test ! -e /tmp/outerfile.txt # Confirm that creating a file in home works -runas testuser systemd-run --user --unit=test-unprotected-home \ +runas testuser systemd-run --wait --user --unit=test-unprotected-home \ -P touch /home/testuser/works.txt test -e /home/testuser/works.txt # Confirm that creating a file in home is blocked under read-only -runas testuser systemd-run --user --unit=test-protect-home-read-only \ +runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \ -p PrivateUsers=yes -p ProtectHome=read-only \ -P bash -c ' test -e /home/testuser/works.txt @@ -40,12 +40,12 @@ runas testuser systemd-run --user --unit=test-protect-home-read-only \ test ! -e /home/testuser/blocked.txt # Check that tmpfs hides the whole directory -runas testuser systemd-run --user --unit=test-protect-home-tmpfs \ +runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \ -p PrivateUsers=yes -p ProtectHome=tmpfs \ -P test ! -e /home/testuser # Confirm that home, /root, and /run/user are inaccessible under "yes" -runas testuser systemd-run --user --unit=test-protect-home-yes \ +runas testuser systemd-run --wait --user --unit=test-protect-home-yes \ -p PrivateUsers=yes -p ProtectHome=yes \ -P bash -c ' test "$(stat -c %a /home)" = "0" @@ -57,7 +57,7 @@ runas testuser systemd-run --user --unit=test-protect-home-yes \ # namespace (no CAP_SETGID in the parent namespace to write the additional # mapping of the user supplied group and thus cannot change groups to an # unmapped group ID) -! runas testuser systemd-run --user --unit=test-group-fail \ +! runas testuser systemd-run --wait --user --unit=test-group-fail \ -p PrivateUsers=yes -p Group=daemon \ -P true diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service new file mode 100644 index 000000000..bd4dd728a --- /dev/null +++ b/test/units/testsuite-44.service @@ -0,0 +1,12 @@ +[Unit] +Description=TESTSUITE-44-LOG-NAMESPACE +Before=getty-pre.target +Wants=getty-pre.target +Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket +After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot +LogTarget=foobar diff --git a/test/TEST-44-LOG-NAMESPACE/testsuite.sh b/test/units/testsuite-44.sh similarity index 100% rename from test/TEST-44-LOG-NAMESPACE/testsuite.sh rename to test/units/testsuite-44.sh diff --git a/test/units/testsuite-46.service b/test/units/testsuite-46.service new file mode 100644 index 000000000..7698f3597 --- /dev/null +++ b/test/units/testsuite-46.service @@ -0,0 +1,12 @@ +[Unit] +Description=TEST-46-HOMED +Wants=getty-pre.target +Before=getty-pre.target +Wants=systemd-homed.service +After=systemd-homed.service + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot +NotifyAccess=all diff --git a/test/TEST-46-HOMED/testsuite.sh b/test/units/testsuite-46.sh similarity index 80% rename from test/TEST-46-HOMED/testsuite.sh rename to test/units/testsuite-46.sh index 9ef9f30f1..00bbdf507 100755 --- a/test/TEST-46-HOMED/testsuite.sh +++ b/test/units/testsuite-46.sh @@ -9,9 +9,16 @@ if ! test -x /usr/bin/homectl ; then fi inspect() { + # As updating disk-size-related attributes can take some time on + # some filesystems, let's drop these fields before comparing the + # outputs to avoid unexpected fails. To see the full outputs of both + # homectl & userdbctl (for debugging purposes) drop the fields just + # before the comparison. homectl inspect $1 | tee /tmp/a userdbctl user $1 | tee /tmp/b - cmp /tmp/a /tmp/b + + local PATTERN='/^\s*Disk (Size|Free|Floor|Ceiling):/d' + diff <(sed -r "$PATTERN" /tmp/a) <(sed -r "$PATTERN" /tmp/b) rm /tmp/a /tmp/b } diff --git a/test/units/testsuite-47-repro.service b/test/units/testsuite-47-repro.service new file mode 100644 index 000000000..655eea68e --- /dev/null +++ b/test/units/testsuite-47-repro.service @@ -0,0 +1,7 @@ +[Unit] +Description=Issue 14566 Repro + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +ExecStopPost=/bin/true +KillMode=mixed diff --git a/test/TEST-47-ISSUE-14566/repro.sh b/test/units/testsuite-47-repro.sh similarity index 71% rename from test/TEST-47-ISSUE-14566/repro.sh rename to test/units/testsuite-47-repro.sh index 521760225..8c34289c5 100755 --- a/test/TEST-47-ISSUE-14566/repro.sh +++ b/test/units/testsuite-47-repro.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash sleep infinity & echo $! > /leakedtestpid diff --git a/test/units/testsuite-47.service b/test/units/testsuite-47.service new file mode 100644 index 000000000..3816c57ee --- /dev/null +++ b/test/units/testsuite-47.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-47-ISSUE-14566 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/TEST-47-ISSUE-14566/testsuite.sh b/test/units/testsuite-47.sh similarity index 68% rename from test/TEST-47-ISSUE-14566/testsuite.sh rename to test/units/testsuite-47.sh index df8d0e960..50034cf3d 100755 --- a/test/TEST-47-ISSUE-14566/testsuite.sh +++ b/test/units/testsuite-47.sh @@ -1,18 +1,18 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex set -o pipefail systemd-analyze log-level debug systemd-analyze log-target console -systemctl start issue_14566_test -sleep 1 -systemctl status issue_14566_test +systemctl start testsuite-47-repro +sleep 4 +systemctl status testsuite-47-repro leaked_pid=$(cat /leakedtestpid) -systemctl stop issue_14566_test -sleep 1 +systemctl stop testsuite-47-repro +sleep 4 # Leaked PID will still be around if we're buggy. # I personally prefer to see 42. diff --git a/test/units/testsuite-48.service b/test/units/testsuite-48.service new file mode 100644 index 000000000..9dc50ab15 --- /dev/null +++ b/test/units/testsuite-48.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-48-START-STOP-NO-RELOAD + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-48.sh b/test/units/testsuite-48.sh new file mode 100755 index 000000000..03231e71b --- /dev/null +++ b/test/units/testsuite-48.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex + +cat > /run/systemd/system/testservice-48.target < /run/systemd/system/testservice-48.service < /run/systemd/system/testservice-48.service < /run/systemd/system/testservice-48.target < /run/systemd/system/testservice-48.service < /testok + +exit 0 diff --git a/test/units/testsuite-49.service b/test/units/testsuite-49.service new file mode 100644 index 000000000..f47177106 --- /dev/null +++ b/test/units/testsuite-49.service @@ -0,0 +1,6 @@ +[Unit] +Description=TEST-49-UDEV-EVENT-TIMEOUT + +[Service] +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-49.sh b/test/units/testsuite-49.sh new file mode 100755 index 000000000..ffa980164 --- /dev/null +++ b/test/units/testsuite-49.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -ex + +test_rule="/run/udev/rules.d/49-test.rules" + +setup() { + mkdir -p "${test_rule%/*}" + cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bckp + echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' > "${test_rule}" + echo "event_timeout=30" >> /etc/udev/udev.conf + echo "timeout_signal=SIGABRT" >> /etc/udev/udev.conf + + systemctl restart systemd-udevd.service +} + +teardown() { + set +e + + mv -f /etc/udev/udev.conf.bckp /etc/udev/udev.conf + rm -f "$test_rule" + systemctl restart systemd-udevd.service +} + +run_test() { + since="$(date +%T)" + + echo add > /sys/class/net/lo/uevent + + for n in {1..20}; do + sleep 5 + if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then + return 0 + fi + done + + return 1 +} + +trap teardown EXIT + +setup +run_test + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service new file mode 100644 index 000000000..5a10a6418 --- /dev/null +++ b/test/units/testsuite-50.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-50-DISSECT + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh new file mode 100755 index 000000000..81e48e0ad --- /dev/null +++ b/test/units/testsuite-50.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -ex +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +cleanup() +{ + if [ -z "${image_dir}" ]; then + return + fi + rm -rf "${image_dir}" +} + +cd /tmp + +image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)" +if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then + echo "mktemp under /tmp failed" + exit 1 +fi + +trap cleanup EXIT + +cp /usr/share/minimal.* "${image_dir}/" +image="${image_dir}/minimal" +roothash="$(cat ${image}.roothash)" + +/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity" +/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1" +/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release + +mv ${image}.verity ${image}.fooverity +mv ${image}.roothash ${image}.foohash +/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity" +/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1" +/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release +mv ${image}.fooverity ${image}.verity +mv ${image}.foohash ${image}.roothash + +mkdir -p ${image_dir}/mount ${image_dir}/mount2 +/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release +cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1" +# Verity volume should be shared (opened only once) +/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount2 +verity_count=$(ls -1 /dev/mapper/ | grep -c verity) +# In theory we should check that count is exactly one. In practice, libdevmapper +# randomly and unpredictably fails with an unhelpful EINVAL when a device is open +# (and even mounted and in use), so best-effort is the most we can do for now +if [ ${verity_count} -lt 1 ]; then + echo "Verity device ${image}.raw not found in /dev/mapper/" + exit 1 +fi +umount ${image_dir}/mount +umount ${image_dir}/mount2 + +systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" +mv ${image}.verity ${image}.fooverity +mv ${image}.roothash ${image}.foohash +systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" +systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" +mv ${image}.fooverity ${image}.verity +mv ${image}.foohash ${image}.roothash + +# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2 +machine="$(uname -m)" +if [ "${machine}" = "x86_64" ]; then + root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 +elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then + root_guid=44479540-f297-41b2-9af7-d131d5f0458a + verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76 +elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then + root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae + verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820 +elif [ "${machine}" = "arm" ]; then + root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3 + verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6 +elif [ "${machine}" = "ia64" ]; then + root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97 + verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571 +else + echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me" + exit 1 +fi +# du rounds up to block size, which is more helpful for partitioning +root_size="$(du -k ${image}.raw | cut -f1)" +verity_size="$(du -k ${image}.verity | cut -f1)" +# 4MB seems to be the minimum size blkid will accept, below that probing fails +dd if=/dev/zero of=${image}.gpt bs=512 count=$((8192+${root_size}*2+${verity_size}*2)) +# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB +# so do some basic rounding up if the minimal image is more than 1 MB +if [ ${root_size} -ge 1024 ]; then + root_size="$((${root_size}/1024 + 1))MiB" +else + root_size="${root_size}KiB" +fi +verity_size="${verity_size}KiB" +uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)" +echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt +uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)" +echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append +sfdisk --part-label ${image}.gpt 1 "Root Partition" +sfdisk --part-label ${image}.gpt 2 "Verity Partition" +loop="$(losetup --show -P -f ${image}.gpt)" +dd if=${image}.raw of=${loop}p1 +dd if=${image}.verity of=${loop}p2 +losetup -d ${loop} + +/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1" +/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2" +/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1" +/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release + +/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release +cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release +cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1" +umount ${image_dir}/mount + +systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" + +echo OK > /testok + +exit 0 diff --git a/test/units/testsuite-51-repro-1.service b/test/units/testsuite-51-repro-1.service new file mode 100644 index 000000000..96ecabe23 --- /dev/null +++ b/test/units/testsuite-51-repro-1.service @@ -0,0 +1,9 @@ +[Unit] +Description=Issue 16115 Repro with on-abnormal + +[Service] +Type=simple +Restart=on-abnormal +ExecCondition=/bin/false +ExecStart=sleep 100 +RestartSec=1 diff --git a/test/units/testsuite-51-repro-2.service b/test/units/testsuite-51-repro-2.service new file mode 100644 index 000000000..6015ad808 --- /dev/null +++ b/test/units/testsuite-51-repro-2.service @@ -0,0 +1,9 @@ +[Unit] +Description=Issue 16115 Repro with on-failure + +[Service] +Type=simple +Restart=on-failure +ExecCondition=/bin/false +ExecStart=sleep 100 +RestartSec=1 diff --git a/test/units/testsuite-51.service b/test/units/testsuite-51.service new file mode 100644 index 000000000..903dc9ab9 --- /dev/null +++ b/test/units/testsuite-51.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-51-ISSUE-16115 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-51.sh b/test/units/testsuite-51.sh new file mode 100755 index 000000000..246412a07 --- /dev/null +++ b/test/units/testsuite-51.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +systemctl start testsuite-51-repro-1 +systemctl start testsuite-51-repro-2 +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" ]] + +touch /testok diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service new file mode 100644 index 000000000..d4dd8cc72 --- /dev/null +++ b/test/units/testsuite-53.service @@ -0,0 +1,7 @@ +[Unit] +Description=TEST-53-ISSUE-16347 + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-53.sh b/test/units/testsuite-53.sh new file mode 100755 index 000000000..3536c2427 --- /dev/null +++ b/test/units/testsuite-53.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -ex +set -o pipefail + +>/failed + +# Reset host date to current time, 3 days in the past. +date -s "-3 days" + +# Run a timer for every 15 minutes. +systemd-run --unit test-timer --on-calendar "*:0/15:0" true + +next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value) +next_elapsed=$(date -d "${next_elapsed}" +%s) +now=$(date +%s) +time_delta=$((next_elapsed - now)) + +# Check that the timer will elapse in less than 20 minutes. +((0 < time_delta && time_delta < 1200)) || { + echo 'Timer elapse outside of the expected 20 minute window.' + echo " next_elapsed=${next_elapsed}" + echo " now=${now}" + echo " time_delta=${time_delta}" + echo '' +} >>/failed + +if test ! -s /failed ; then + rm -f /failed + touch /testok +fi diff --git a/test/testsuite.target b/test/units/testsuite.target similarity index 100% rename from test/testsuite.target rename to test/units/testsuite.target diff --git a/test/units/timers.target b/test/units/timers.target new file mode 100644 index 000000000..b1aa8c797 --- /dev/null +++ b/test/units/timers.target @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Timers +Documentation=man:systemd.special(7) + +DefaultDependencies=no +Conflicts=shutdown.target diff --git a/test/unit-.service.d/10-override.conf b/test/units/unit-.service.d/10-override.conf similarity index 100% rename from test/unit-.service.d/10-override.conf rename to test/units/unit-.service.d/10-override.conf diff --git a/test/unit-with-.service.d/20-override.conf b/test/units/unit-with-.service.d/20-override.conf similarity index 100% rename from test/unit-with-.service.d/20-override.conf rename to test/units/unit-with-.service.d/20-override.conf diff --git a/test/unit-with-multiple-.service.d/20-override.conf b/test/units/unit-with-multiple-.service.d/20-override.conf similarity index 100% rename from test/unit-with-multiple-.service.d/20-override.conf rename to test/units/unit-with-multiple-.service.d/20-override.conf diff --git a/test/unit-with-multiple-.service.d/30-override.conf b/test/units/unit-with-multiple-.service.d/30-override.conf similarity index 100% rename from test/unit-with-multiple-.service.d/30-override.conf rename to test/units/unit-with-multiple-.service.d/30-override.conf diff --git a/test/unit-with-multiple-dashes.service b/test/units/unit-with-multiple-dashes.service similarity index 100% rename from test/unit-with-multiple-dashes.service rename to test/units/unit-with-multiple-dashes.service diff --git a/test/unit-with-multiple-dashes.service.d/10-override.conf b/test/units/unit-with-multiple-dashes.service.d/10-override.conf similarity index 100% rename from test/unit-with-multiple-dashes.service.d/10-override.conf rename to test/units/unit-with-multiple-dashes.service.d/10-override.conf diff --git a/test/unstoppable.service b/test/units/unstoppable.service similarity index 100% rename from test/unstoppable.service rename to test/units/unstoppable.service diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build index e77f46d06..0a9582d8b 100644 --- a/tmpfiles.d/meson.build +++ b/tmpfiles.d/meson.build @@ -8,6 +8,7 @@ tmpfiles = [['home.conf', ''], ['systemd-nspawn.conf', 'ENABLE_MACHINED'], ['systemd-tmp.conf', ''], ['portables.conf', 'ENABLE_PORTABLED'], + ['systemd-pstore.conf', 'ENABLE_PSTORE'], ['tmp.conf', ''], ['x11.conf', ''], ['legacy.conf', 'HAVE_SYSV_COMPAT'], diff --git a/tmpfiles.d/systemd-pstore.conf b/tmpfiles.d/systemd-pstore.conf new file mode 100644 index 000000000..8b6a1dafc --- /dev/null +++ b/tmpfiles.d/systemd-pstore.conf @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# The systemd-pstore.service(1) archives the contents of /sys/fs/pstore +# upon boot so that there is room for a subsequent dump. This service +# is enabled with: +# systemctl enable systemd-pstore +# +# With the service enabled, the kernel still needs to be configured +# to write data into the pstore. The kernel has two parameters, +# crash_kexec_post_notifiers and printk.always_kmsg_dump, that +# control writes into pstore. +# +# The crash_kexec_post_notifiers parameter enables the kernel to write +# dmesg (including stack trace) into pstore upon a panic, and +# printk.always_kmsg_dump parameter enables the kernel to write dmesg +# upon a normal shutdown (shutdown, reboot, halt). +# +# To configure the kernel parameters, uncomment the appropriate +# line(s) below. The value written is either 'Y' to enable the +# kernel parameter, or 'N' to disable the kernel parameter. +# +# After making a change to this file, do: +# systemd-tmpfiles --create path/to/tmpfiles.d/systemd-pstore.conf +# +# These changes are automatically applied on future re-boots. + +d /var/lib/systemd/pstore 0755 root root 14d +#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y +w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y diff --git a/tools/meson-autosuspend-update.sh b/tools/autosuspend-update.sh similarity index 100% rename from tools/meson-autosuspend-update.sh rename to tools/autosuspend-update.sh diff --git a/tools/meson-check-api-docs.sh b/tools/check-api-docs.sh similarity index 73% rename from tools/meson-check-api-docs.sh rename to tools/check-api-docs.sh index bd320562a..1094101e0 100755 --- a/tools/meson-check-api-docs.sh +++ b/tools/check-api-docs.sh @@ -6,7 +6,16 @@ sd_total=0 udev_good=0 udev_total=0 -for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | sort -u` ; do +deprecated=" + -e sd_bus_try_close + -e sd_bus_process_priority + -e sd_bus_message_get_priority + -e sd_bus_message_set_priority + -e sd_seat_can_multi_session + -e sd_journal_open_container +" + +for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | grep -wv $deprecated | sort -u` ; do if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then echo "✓ Symbol $symbol() is documented." good=1 diff --git a/tools/meson-check-compilation.sh b/tools/check-compilation.sh similarity index 100% rename from tools/meson-check-compilation.sh rename to tools/check-compilation.sh diff --git a/tools/meson-check-help.sh b/tools/check-help.sh similarity index 100% rename from tools/meson-check-help.sh rename to tools/check-help.sh diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py index 4e8593f32..66018a54f 100644 --- a/tools/gdb-sd_dump_hashmaps.py +++ b/tools/gdb-sd_dump_hashmaps.py @@ -1,79 +1,77 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1+ -from __future__ import print_function - import gdb class sd_dump_hashmaps(gdb.Command): - "dump systemd's hashmaps" + "dump systemd's hashmaps" - def __init__(self): - super(sd_dump_hashmaps, self).__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) + def __init__(self): + super().__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE) - def invoke(self, arg, from_tty): - d = gdb.parse_and_eval("hashmap_debug_list") - all_entry_sizes = gdb.parse_and_eval("all_entry_sizes") - all_direct_buckets = gdb.parse_and_eval("all_direct_buckets") - uchar_t = gdb.lookup_type("unsigned char") - ulong_t = gdb.lookup_type("unsigned long") - debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") + def invoke(self, arg, from_tty): + d = gdb.parse_and_eval("hashmap_debug_list") + hashmap_type_info = gdb.parse_and_eval("hashmap_type_info") + uchar_t = gdb.lookup_type("unsigned char") + ulong_t = gdb.lookup_type("unsigned long") + debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") - print("type, hash, indirect, entries, max_entries, buckets, creator") - while d: - h = gdb.parse_and_eval("(HashmapBase*)((char*)%d - %d)" % (int(d.cast(ulong_t)), debug_offset)) + print("type, hash, indirect, entries, max_entries, buckets, creator") + while d: + h = gdb.parse_and_eval(f"(HashmapBase*)((char*){int(d.cast(ulong_t))} - {debug_offset})") - if h["has_indirect"]: - storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer()) - n_entries = h["indirect"]["n_entries"] - n_buckets = h["indirect"]["n_buckets"] - else: - storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer()) - n_entries = h["n_direct_entries"] - n_buckets = all_direct_buckets[int(h["type"])]; + if h["has_indirect"]: + storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer()) + n_entries = h["indirect"]["n_entries"] + n_buckets = h["indirect"]["n_buckets"] + else: + storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer()) + n_entries = h["n_direct_entries"] + n_buckets = hashmap_type_info[h["type"]]["n_direct_buckets"] - t = ["plain", "ordered", "set"][int(h["type"])] + t = ["plain", "ordered", "set"][int(h["type"])] - print("{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"])) + print(f'{t}, {h["hash_ops"]}, {bool(h["has_indirect"])}, {n_entries}, {d["max_entries"]}, {n_buckets}, {d["func"].string()}, {d["file"].string()}:{d["line"]}') - if arg != "" and n_entries > 0: - dib_raw_addr = storage_ptr + (all_entry_sizes[h["type"]] * n_buckets) + if arg != "" and n_entries > 0: + dib_raw_addr = storage_ptr + hashmap_type_info[h["type"]]["entry_size"] * n_buckets - histogram = {} - for i in xrange(0, n_buckets): - dib = int(dib_raw_addr[i]) - histogram[dib] = histogram.get(dib, 0) + 1 + histogram = {} + for i in range(0, n_buckets): + dib = int(dib_raw_addr[i]) + histogram[dib] = histogram.get(dib, 0) + 1 - for dib in sorted(iter(histogram)): - if dib != 255: - print("{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries)) - else: - print("{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets)) - print("mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries)) + for dib in sorted(histogram): + if dib != 255: + print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_entries):.0%} of entries") + else: + print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_buckets):.0%} of slots") + s = sum(dib*count for (dib, count) in histogram.items() if dib != 255) / n_entries + print(f"mean DIB of entries: {s}") - blocks = [] - current_len = 1 - prev = int(dib_raw_addr[0]) - for i in xrange(1, n_buckets): - dib = int(dib_raw_addr[i]) - if (dib == 255) != (prev == 255): - if prev != 255: - blocks += [[i, current_len]] - current_len = 1 - else: - current_len += 1 + blocks = [] + current_len = 1 + prev = int(dib_raw_addr[0]) + for i in range(1, n_buckets): + dib = int(dib_raw_addr[i]) + if (dib == 255) != (prev == 255): + if prev != 255: + blocks += [[i, current_len]] + current_len = 1 + else: + current_len += 1 - prev = dib - if prev != 255: - blocks += [[i, current_len]] - # a block may be wrapped around - if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: - blocks[0][1] += blocks[-1][1] - blocks = blocks[0:-1] - print("max block: {}".format(max(blocks, key=lambda a: a[1]))) - print("sum block lens: {}".format(sum(b[1] for b in blocks))) - print("mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks)))) + prev = dib + if prev != 255: + blocks += [[i, current_len]] + # a block may be wrapped around + if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: + blocks[0][1] += blocks[-1][1] + blocks = blocks[0:-1] + print("max block: {}".format(max(blocks, key=lambda a: a[1]))) + print("sum block lens: {}".format(sum(b[1] for b in blocks))) + print("mean block len: {}".format(sum(b[1] for b in blocks) / len(blocks))) - d = d["debug_list_next"] + d = d["debug_list_next"] sd_dump_hashmaps() diff --git a/tools/git-contrib.sh b/tools/git-contrib.sh new file mode 100755 index 000000000..cc0f99169 --- /dev/null +++ b/tools/git-contrib.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +git shortlog -s `git describe --abbrev=0`.. | \ + awk '{ $1=""; print $0 "," }' | \ + sort -u diff --git a/tools/meson-hwdb-update.sh b/tools/hwdb-update.sh similarity index 100% rename from tools/meson-hwdb-update.sh rename to tools/hwdb-update.sh diff --git a/tools/make-autosuspend-rules.py b/tools/make-autosuspend-rules.py index 25b261ea0..a20edc0f3 100755 --- a/tools/make-autosuspend-rules.py +++ b/tools/make-autosuspend-rules.py @@ -1,14 +1,24 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1+ -# Generate autosuspend rules for devices that have been whitelisted (IE tested) -# by the Chromium OS team. Please keep this script in sync with: +# Generate autosuspend rules for devices that have been tested to work properly +# with autosuspend by the Chromium OS team. Based on # https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py -import sys import chromiumos.gen_autosuspend_rules -if __name__ == '__main__': - if len(sys.argv) > 1: - sys.stdout = open(sys.argv[1], 'w') - chromiumos.gen_autosuspend_rules.main() +print('# pci:v<00VENDOR>d<00DEVICE> (8 uppercase hexadecimal digits twice)') +for entry in chromiumos.gen_autosuspend_rules.PCI_IDS: + vendor, device = entry.split(':') + vendor = int(vendor, 16) + device = int(device, 16) + print('pci:v{:08X}d{:08X}*'.format(vendor, device)) + +print('# usb:vp (4 uppercase hexadecimal digits twice)') +for entry in chromiumos.gen_autosuspend_rules.USB_IDS: + vendor, product = entry.split(':') + vendor = int(vendor, 16) + product = int(product, 16) + print('usb:v{:04X}p{:04X}*'.format(vendor, product)) + +print(' ID_AUTOSUSPEND=1') diff --git a/tools/make-directive-index.py b/tools/make-directive-index.py index da10575f3..b04281490 100755 --- a/tools/make-directive-index.py +++ b/tools/make-directive-index.py @@ -7,166 +7,6 @@ import re from xml_helper import xml_parse, xml_print, tree from copy import deepcopy -TEMPLATE = '''\ - - - - systemd.directives - systemd - - - - systemd.directives - 7 - - - - systemd.directives - Index of configuration directives - - - - Unit directives - - Directives for configuring units, used in unit - files. - - - - - - Options on the kernel command line - - Kernel boot options for configuring the behaviour of the - systemd process. - - - - - - Environment variables - - Environment variables understood by the systemd manager - and other programs and environment variable-compatible settings. - - - - - - EFI variables - - EFI variables understood by - systemd-boot7 - and other programs. - - - - - - UDEV directives - - Directives for configuring systemd units through the - udev database. - - - - - - Network directives - - Directives for configuring network links through the - net-setup-link udev builtin and networks through - systemd-networkd. - - - - - - Journal fields - - Fields in the journal events with a well known meaning. - - - - - - PAM configuration directives - - Directives for configuring PAM behaviour. - - - - - - <filename>/etc/crypttab</filename> and - <filename>/etc/fstab</filename> options - - Options which influence mounted filesystems and - encrypted volumes. - - - - - - <citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry> - directives - - Directives for configuring systemd-nspawn containers. - - - - - - Program configuration options - - Directives for configuring the behaviour of the - systemd process and other tools through configuration files. - - - - - - Command line options - - Command-line options accepted by programs in the - systemd suite. - - - - - - Constants - - Various constant used and/or defined by systemd. - - - - - - Miscellaneous options and directives - - Other configuration elements which don't fit in - any of the above groups. - - - - - - Files and directories - - Paths and file names referred to in the - documentation. - - - - - - Colophon - - - -''' - COLOPHON = '''\ This index contains {count} entries in {sections} sections, referring to {pages} individual manual pages. @@ -180,9 +20,10 @@ def _extract_directives(directive_groups, formatting, page): storopt = directive_groups['options'] for variablelist in t.iterfind('.//variablelist'): klass = variablelist.attrib.get('class') + searchpath = variablelist.attrib.get('xpath','./varlistentry/term/varname') storvar = directive_groups[klass or 'miscellaneous'] #