diff --git a/.clang-format-ignore b/.clang-format-ignore index 97dc6786a..22f8ad360 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,3 +1,4 @@ Makefile* +libmultipath/dict.c libmultipath/hwtable.c multipathd/cli.h diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index a5856bcc8..dd39ed672 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -6,6 +6,7 @@ alloc alltgpt alua aptpl +ASAN ascq ata autoconfig @@ -13,6 +14,7 @@ autodetected autoresize barbie BINDIR +bitfield blkid bmarzins Bsymbolic @@ -58,14 +60,15 @@ EAGAIN ECKD emc Engenio -Equal EVPD +Exos failback failover fds fexceptions FFFFFFFF fge +fno followover forcequeueing fpin @@ -77,6 +80,7 @@ getrlimit getuid github gitlab +google GPT hbtl hds @@ -122,6 +126,7 @@ libudevdir liburcu linux LIO +lld lpthread Lun lvm @@ -141,13 +146,13 @@ multipathing multipaths multiqueue mwilck +NFINIDAT NOLOG nompath NOSCAN Nosync -NVMe nvme -NFINIDAT +Nytro OBJDEPS oneshot ontap @@ -157,8 +162,10 @@ OPTFLAGS paramp partx pathgroup +pathlist petabytes pgpolicy +pgvec plugindir PNR ppc @@ -193,6 +200,7 @@ rpmbuild rport rtpi rtprio +sanitizers sas sbp scsi @@ -219,11 +227,11 @@ suse svg switchgroup sys +SYSDIR sysfs sysinit tcp terabytes -SYSDIR TESTDEPS testname tgill @@ -240,7 +248,6 @@ udevd uevent uid unitdir -unregistering unsetmarginal unsetprkey unsetprstatus diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index f2ef14240..55d4742eb 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -10,6 +10,11 @@ #commit \b[0-9a-f]{7}\b +# Generic Hex numbers +\b0x[0-9a-f]{4}\b +\b0x[0-9a-f]{8}\b +\b0x[0-9a-f]{16}\b + # WWNN/WWPN (NAA identifiers) \b(?:0x)?10[0-9a-f]{14}\b \b(?:0x|3)?[25][0-9a-f]{15}\b diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml index 8a045b05c..8f9a9dbf3 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -15,12 +15,18 @@ on: jobs: reference-abi: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - name: get parent tag + - name: get parent tag (push) run: > echo ${{ github.ref }} | sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'push' }} + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'pull_request' }} - name: assert parent tag run: /bin/false if: ${{ env.PARENT_TAG == '' }} @@ -32,12 +38,13 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: checkout ${{ env.PARENT_TAG }} uses: actions/checkout@v4 with: ref: ${{ env.PARENT_TAG }} - name: build ABI for ${{ env.PARENT_TAG }} - run: make -j$(grep -c ^processor /proc/cpuinfo) -Orecurse abi + run: make -j$(nproc) -Orecurse abi - name: save ABI uses: actions/upload-artifact@v4 with: @@ -45,20 +52,26 @@ jobs: path: abi check-abi: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: reference-abi steps: - - name: get parent tag + - name: get parent tag (push) run: > echo ${{ github.ref }} | sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'push' }} + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: ${{ github.event_name == 'pull_request' }} - name: assert parent tag run: /bin/false if: ${{ env.PARENT_TAG == '' }} - - name: checkout ${{ env.PARENT_TAG }} + - name: checkout ${{ github.ref }} uses: actions/checkout@v4 with: - ref: ${{ env.PARENT_TAG }} + ref: ${{ github.ref }} - name: download ABI for ${{ env.PARENT_TAG }} id: download_abi uses: actions/download-artifact@v4 @@ -74,9 +87,10 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: check ABI of ${{ github.ref }} against ${{ env.PARENT_TAG }} id: check_abi - run: make -j$(grep -c ^processor /proc/cpuinfo) -Orecurse abi-test + run: make -j$(nproc) -Orecurse abi-test continue-on-error: true - name: save differences if: ${{ steps.check_abi.outcome != 'success' }} diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml index b1282493f..adc2081f1 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -18,7 +18,7 @@ env: jobs: save-and-test-ABI: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: set ABI branch if: ${{ env.ABI_BRANCH == '' }} @@ -42,8 +42,9 @@ jobs: gcc make pkg-config abigail-tools libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: create ABI - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) abi.tar.gz + run: make -Orecurse -j$(nproc) abi.tar.gz - name: save ABI uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml index 4f568131c..2fb5327d4 100644 --- a/.github/workflows/build-and-unittest.yaml +++ b/.github/workflows/build-and-unittest.yaml @@ -13,7 +13,7 @@ on: - 'stable-*' jobs: jammy: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -46,16 +46,16 @@ jobs: if: ${{ matrix.cc == 'clang' }} - name: build run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) READLINE=${{ matrix.rl }} OPTFLAGS="$OPT" - name: test run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) OPTFLAGS="$OPT" test - name: valgrind-test id: valgrind run: > - make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) + make -Orecurse -j$(nproc) OPTFLAGS="$OPT" valgrind-test continue-on-error: true - name: valgrind-results @@ -92,12 +92,12 @@ jobs: - name: set CC run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV - name: build - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }} + run: make -Orecurse -j$(nproc) READLINE=${{ matrix.rl }} - name: test - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) test + run: make -Orecurse -j$(nproc) test - name: valgrind-test id: valgrind - run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) valgrind-test + run: make -Orecurse -j$(nproc) valgrind-test continue-on-error: true - name: valgrind-results run: cat tests/*.vgr diff --git a/.github/workflows/coverity.yaml b/.github/workflows/coverity.yaml index 678d0d2a3..30e5471d3 100644 --- a/.github/workflows/coverity.yaml +++ b/.github/workflows/coverity.yaml @@ -6,7 +6,7 @@ on: jobs: upload-coverity-scan: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v4 @@ -16,6 +16,7 @@ jobs: gcc make pkg-config libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev - name: download coverity run: > curl -o cov-analysis-linux64.tar.gz @@ -32,7 +33,7 @@ jobs: - name: build with cov-build run: > PATH="$PWD/coverity/bin:$PATH" - cov-build --dir cov-int make -Orecurse -j"$(grep -c ^processor /proc/cpuinfo)" + cov-build --dir cov-int make -Orecurse -j"$(nproc)" - name: pack results run: tar cfz multipath-tools.tgz cov-int - name: submit results diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index a25179710..b232b60c1 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -27,7 +27,7 @@ on: jobs: cross-build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -43,7 +43,7 @@ jobs: - name: checkout uses: actions/checkout@v4 - name: build - run: make -j -Orecurse test-progs.tar + run: make -j$(nproc) -Orecurse test-progs.tar - name: upload binary archive uses: actions/upload-artifact@v4 with: @@ -52,7 +52,7 @@ jobs: overwrite: true test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: cross-build strategy: fail-fast: false @@ -94,7 +94,7 @@ jobs: pull-params: "--platform linux/${{ env.CONTAINER_ARCH }}" root-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: cross-build strategy: fail-fast: false diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index 2a30a8605..b8c770e40 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -26,7 +26,7 @@ on: jobs: build-old: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -37,14 +37,10 @@ jobs: - ubuntu-trusty arch: [386, arm/v7] include: - - os: debian-trixie - arch: aarch64 - os: debian-trixie arch: s390x - os: debian-trixie arch: ppc64le - - os: debian-bookworm - arch: aarch64 - os: debian-bookworm arch: s390x - os: debian-bookworm @@ -57,6 +53,7 @@ jobs: with: image: tonistiigi/binfmt:latest - name: compile and run unit tests + id: test uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} @@ -65,3 +62,23 @@ jobs: command: test params: "--platform linux/${{ matrix.arch }}" pull-params: "--platform linux/${{ matrix.arch }}" + continue-on-error: true + - name: create binary archive + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + guest-dir: /build + host-dir: ${{ github.workspace }} + command: test-progs.tar + params: "--platform linux/${{ matrix.arch }}" + pull-params: "--platform linux/${{ matrix.arch }}" + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.arch }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml index c453446a6..806275a13 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -30,7 +30,7 @@ on: jobs: build-current: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -39,12 +39,15 @@ jobs: - debian-sid - fedora-rawhide - opensuse-tumbleweed - arch: [amd64, ppc64le, aarch64, s390x, 386, arm/v7] + arch: [ppc64le, s390x, 386, arm/v7] exclude: - os: fedora-rawhide arch: 386 - os: fedora-rawhide arch: arm/v7 + include: + - os: alpine + arch: aarch64 steps: - name: checkout uses: actions/checkout@v4 @@ -53,6 +56,7 @@ jobs: with: image: tonistiigi/binfmt:latest - name: compile and run unit tests + id: test uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} @@ -61,4 +65,23 @@ jobs: command: test params: "--platform linux/${{ matrix.arch }}" pull-params: "--platform linux/${{ matrix.arch }}" - + continue-on-error: true + - name: create binary archive + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + guest-dir: /build + host-dir: ${{ github.workspace }} + command: test-progs.tar + params: "--platform linux/${{ matrix.arch }}" + pull-params: "--platform linux/${{ matrix.arch }}" + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.arch }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 6b38fa712..9e89780b1 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -26,7 +26,6 @@ on: jobs: stable: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -36,8 +35,15 @@ jobs: - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} steps: - name: checkout uses: actions/checkout@v4 @@ -45,24 +51,29 @@ jobs: - name: set archive name # Leap containers have cpio but not tar run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV - if: ${{ matrix.os == 'opensuse-leap' }} + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - name: set archive name run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV - if: ${{ matrix.os != 'opensuse-leap' }} + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} - name: build and test + id: test if: ${{ matrix.os != 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j -Orecurse test + command: -j$(nproc) -Orecurse test + continue-on-error: true + - name: build and test (jessie) + id: test_jessie # On jessie, we use libreadline 5 (no licensing issue) if: ${{ matrix.os == 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j -Orecurse READLINE=libreadline test + command: -j$(nproc) -Orecurse READLINE=libreadline test + continue-on-error: true - name: create ${{ env.ARCHIVE_TGT }} if: ${{ matrix.os != 'debian-jessie' }} @@ -80,44 +91,113 @@ jobs: - name: upload binary archive uses: actions/upload-artifact@v4 with: - name: native-${{ matrix.os }} + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} path: ${{ env.ARCHIVE_TGT }} overwrite: true + - name: fail + run: /bin/false + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} clang: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: os: - - debian-jessie - debian-buster - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + include: + - os: debian-jessie + variant: + arch: x86_64 + runner: ubuntu-24.04 + runs-on: ${{ matrix.variant.runner }} steps: - name: checkout uses: actions/checkout@v4 + - name: set archive name + # Leap containers have cpio but not tar + run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} + - name: set archive name + run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} + - name: clang + id: test if: ${{ matrix.os != 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} params: -e CC=clang - command: -j -Orecurse test + command: -j$(nproc) -Orecurse test + continue-on-error: true - name: clang (jessie) + id: test_jessie if: ${{ matrix.os == 'debian-jessie' }} uses: mosteo-actions/docker-run@v1 with: image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} params: -e CC=clang - command: -j -Orecurse READLINE=libreadline test + command: -j$(nproc) -Orecurse READLINE=libreadline test + continue-on-error: true + + - name: create ${{ env.ARCHIVE_TGT }} + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + command: ${{ env.ARCHIVE_TGT }} + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + - name: create ${{ env.ARCHIVE_TGT }} (jessie) + uses: mosteo-actions/docker-run@v1 + with: + image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} + command: READLINE=libreadline ${{ env.ARCHIVE_TGT }} + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: clang-${{ matrix.os }}-${{ matrix.variant.arch }} + path: ${{ env.ARCHIVE_TGT }} + overwrite: true + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} + + - name: fail + run: /bin/false + if: >- + ${{ ( matrix.os == 'debian-jessie' && + steps.test_jessie.outcome != 'success' ) || + ( matrix.os != 'debian-jessie' && + steps.test.outcome != 'success' ) }} root-test: - runs-on: ubuntu-22.04 needs: stable strategy: fail-fast: false @@ -128,8 +208,15 @@ jobs: - debian-bullseye - debian-bookworm - debian-trixie - - fedora-42 - - opensuse-leap + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} steps: - name: mpath run: sudo modprobe dm_multipath @@ -142,13 +229,13 @@ jobs: - name: download binary archive uses: actions/download-artifact@v4 with: - name: native-${{ matrix.os }} + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} - name: unpack binary archive run: cpio -idv < test-progs.cpio - if: ${{ matrix.os == 'opensuse-leap' }} + if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - name: unpack binary archive run: tar xfmv test-progs.tar - if: ${{ matrix.os != 'opensuse-leap' }} + if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} - name: run root tests uses: mosteo-actions/docker-run@v1 diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml index 503625e04..a4706e52e 100644 --- a/.github/workflows/rolling.yaml +++ b/.github/workflows/rolling.yaml @@ -29,24 +29,50 @@ on: jobs: rolling: - runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: os: - debian-sid - - alpine - opensuse-tumbleweed - fedora-rawhide + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + compiler: + - gcc + - clang + include: + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: gcc + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: clang + runs-on: ${{ matrix.variant.runner }} container: ghcr.io/mwilck/multipath-build-${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v4 - name: build and test - run: make READLINE=libreadline -j -Orecurse test - - name: clean - run: make -j -Orecurse clean - - name: clang - env: - CC: clang - run: make READLINE=libedit -j -Orecurse test + id: test + run: make CC=${{ matrix.compiler }} READLINE=libreadline -j$(nproc) -Orecurse test + continue-on-error: true + - name: create binary archive + run: make test-progs.tar + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/Makefile.inc b/Makefile.inc index 9e3dc4661..c6aa6eda2 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -9,6 +9,12 @@ # Uncomment to disable dmevents polling support # ENABLE_DMEVENTS_POLL = 0 # +# Use ASAN=1 on make command line to enable address sanitizer +ASAN := +# +# Override on make command line if desired +OPT := -O2 + # Readline library to use, libedit, libreadline, or empty # Caution: Using libreadline may make the multipathd binary undistributable, # see https://github.com/opensvc/multipath-tools/issues/36 @@ -102,7 +108,7 @@ SYSTEMD_LIBDEPS := $(if $(SYSTEMD),$(if $(shell test $(SYSTEMD) -gt 209 && echo MODPROBE_UNIT := $(shell test "0$(SYSTEMD)" -lt 245 2>/dev/null || \ echo "modprobe@dm_multipath.service") -OPTFLAGS := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4 +OPTFLAGS := $(OPT) -g $(STACKPROT) --param=ssp-buffer-size=4 # Set WARN_ONLY=1 to avoid compilation erroring out due to warnings. Useful during development. WARN_ONLY := @@ -112,7 +118,7 @@ WARNFLAGS := $(WERROR) -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -W$(ERROR)imp -W$(ERROR)implicit-function-declaration -W$(ERROR)format-security \ $(WNOCLOBBERED) -W$(ERROR)cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS) -CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \ +CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) $(D_CMOCKA_VERSION) \ -D_FILE_OFFSET_BITS=64 \ -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(TGTDIR)$(plugindir)\" \ -DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \ @@ -122,11 +128,11 @@ CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \ -DWSTRINGOP_TRUNCATION=$(if $(WSTRINGOP_TRUNCATION),1,0) \ -MMD -MP CFLAGS := -std=$(C_STD) $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \ - -fexceptions + -fexceptions -fno-strict-aliasing $(if $(ASAN),-fsanitize=address) BIN_CFLAGS := -fPIE -DPIE LIB_CFLAGS := -fPIC SHARED_FLAGS := -shared -LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs +LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs $(if $(ASAN),-lasan) BIN_LDFLAGS := -pie # Source code directories. Don't modify. diff --git a/NEWS.md b/NEWS.md index 6f0018e67..a842648e4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,80 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.14.0, 2026/01 + +### User-visible changes + +#### Automatic removal of disconnected SCSI devices + +This release adds support for automatically purging SCSI devices that become +disconnected at the storage target (e.g. because a LUN became unmapped by an +administrator). It's a long-standing problem that such path devices become +"stale" on the Linux side. When they are re-mapped on the storage side, +possibly with different parameters, the kernel may not correctly detect +the changes. By removing such stale devices, the kernel will be forced to +re-probe them, which fixes this problem. + +The feature is controlled by a new configuration option `purge_disconnected` +that can be set in `multipath.conf` per multipath device, hardware entry, or +globally. The feature is switched off by default. + +When `purge_disconnected` is enabled, SCSI devices for which the storage array +reports a "LUN NOT SUPPORTED" status are removed from the system, preventing +stale device entries from accumulating. + +As this is a new feature, users are advised to test it in their environment +before enabling it in production. + +### Bug fixes + +* Make sure multipathd registers keys all paths of a multipath map after + mpathpersist registered a key for a map. multipathd could fail to do so in + some cases, e.g. if paths become unavailable or available while the + registration was taking place. Fixes 0.13.0. Commits f7d6cd1, 4f3036b. +* Fix `mpathpersist --report-capabilities` output. Fixes 0.5.0. Commit + c8ed5e6. +* Improve error handling when retrying REGISTER AND IGNORE persistent + reservations commands. Fixes 0.13.0. Commit c971036. +* Fix command descriptions in the multipathd man page. Fixes 0.9.2. + Commit f3ba2e7. +* Fix an undefined symbol error with the LLVM lld linker. Commit a298603. + Fixes [#132](https://github.com/opensvc/multipath-tools/issues/132), 0.10.0. +* Fix ISO C23 compatibility issue causing errors with new compilers. + Commit 9f611e2. +* Fix use-after-free error in free_pgvec(). Commit 7a07023. + Fixes [#128](https://github.com/opensvc/multipath-tools/issues/128), 0.12.0. +* Fix memory leak caused by not joining the "init unwinder" thread. + Fixes 0.8.6. Commit 29f262b. +* Fix memory leaks in kpartx. Commits 8c39e60, 02e0933. Fixes any version. +* Fix a particular strict aliasing bug in the bitfield code. + Fixes 0.8.9. Commit f1299f2. This fix isn't strictly necessary any + more after adding `-fno-strict-aliasing` (see below), but it doesn't hurt to + have it. +* Print the warning "setting scsi timeouts is unsupported for protocol" only + once per protocol. Commit 958c826. Fixes 0.9.0. +* Make sure multipath-tools is compiled with the compiler flag + `-fno-strict-aliasing`. This turns out to be necessary because our code + uses techniques like `container_of()` which don't work well with + strict aliasing rules. Commit 1ef540a. + Fixes [#130](https://github.com/opensvc/multipath-tools/issues/130). + +### Other changes + +* Add wrapper code for libudev to avoid potential issues with calling libudev + from a multi-threaded program. Commits 69f6590, 6cc012f. +* Clean up the code for freeing struct path and struct multipath objects. + Specifically, avoid freeing path objects from code that frees multipath + maps, and vice versa. Treat path orphaning and freeing separately. +* Hardware table: add Seagate Exos and Nytro series. +* Avoid joining threads twice with liburcu 0.14.0 and newer. +* CI updates (GitHub workflows). +* Fix CI for cmocka 2.0. Commits 2c52668, f427f8f. + Fixes [#129](https://github.com/opensvc/multipath-tools/issues/129) +* Remove the "hotplug" mode of kpartx, which has been obsolete since udev + replaced legacy hotplug. +* Add the ASAN=1 and OPT= make variables (see README.md). + ## multipath-tools 0.13.0, 2025/10 ### Major rework of the SCSI Persistent Reservation code diff --git a/README.md b/README.md index 530caed7d..888998b7e 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,15 @@ See "Passing standard compiler flags" below for an exception. The following variables can be passed to the `make` command line: * `V=1`: enable verbose build. + * `OPT=`: set optimization flags. You may want to set `OPT="-O0"` for + debugging, for example. The default is `-O2`. Note that it is also + possible to set `OPTFLAGS`, which takes precedence over `OPT`. `OPTFLAGS` + sets additional options by default, which are intended for distribution + build environments to override. For quick customization of the optimization + level, use `OPT`. + * `ASAN=1`: Enable + [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer) + during build for debugging memory allocation. This is off by default. * `plugindir="/some/path"`: directory where libmultipath plugins (path checkers, prioritizers, and foreign multipath support) will be looked up. This used to be the run-time option `multipath_dir` in earlier versions. diff --git a/create-config.mk b/create-config.mk index 3c3ba069e..712cef05c 100644 --- a/create-config.mk +++ b/create-config.mk @@ -77,6 +77,10 @@ URCU_VERSION = $(shell \ $(PKG_CONFIG) --modversion liburcu 2>/dev/null | \ awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }') +CMOCKA_VERSION = $(shell \ + ($(PKG_CONFIG) --modversion cmocka 2>/dev/null || echo "1.1.0" ) | \ + awk -F. '{ printf("-DCMOCKA_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }') + DEFINES := ifneq ($(call check_func,dm_task_no_flush,$(devmapper_incdir)/libdevmapper.h),0) @@ -185,6 +189,7 @@ $(TOPDIR)/config.mk: $(multipathdir)/autoconfig.h @echo "FPIN_SUPPORT := $(FPIN_SUPPORT)" >$@ @echo "FORTIFY_OPT := $(FORTIFY_OPT)" >>$@ @echo "D_URCU_VERSION := $(call URCU_VERSION)" >>$@ + @echo "D_CMOCKA_VERSION := $(call CMOCKA_VERSION)" >>$@ @echo "SYSTEMD := $(SYSTEMD)" >>$@ @echo "ANA_SUPPORT := $(ANA_SUPPORT)" >>$@ @echo "STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)" >>$@ diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index a1495e55e..9bdd20409 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -145,56 +145,6 @@ find_devname_offset (char * device) return (int)(q - device); } -static char * -get_hotplug_device(void) -{ - unsigned int major, minor, off, len; - char *mapname; - char *devname = NULL; - char *device = NULL; - char *var = NULL; - struct stat buf; - - var = getenv("ACTION"); - - if (!var || strcmp(var, "add")) - return NULL; - - /* Get dm mapname for hotpluged device. */ - if (!(devname = getenv("DEVNAME"))) - return NULL; - - if (stat(devname, &buf)) - return NULL; - - major = major(buf.st_rdev); - minor = minor(buf.st_rdev); - - if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */ - return NULL; - - off = find_devname_offset(devname); - len = strlen(mapname); - - /* Dirname + mapname + \0 */ - if (!(device = (char *)malloc(sizeof(char) * (off + len + 1)))) { - free(mapname); - return NULL; - } - - /* Create new device name. */ - snprintf(device, off + 1, "%s", devname); - snprintf(device + off, len + 1, "%s", mapname); - - if (strlen(device) != (off + len)) { - free(device); - free(mapname); - return NULL; - } - free(mapname); - return device; -} - static int check_uuid(char *uuid, char *part_uuid, char **err_msg) { char *map_uuid = strchr(part_uuid, '-'); @@ -227,6 +177,11 @@ xmalloc (size_t size) { return t; } +static void cleanup_charp(char **p) +{ + free(*p); +} + int main(int argc, char **argv){ int i, j, m, n, op, off, arg, c, d, ro=0; @@ -234,14 +189,14 @@ main(int argc, char **argv){ struct slice all; struct pt *ptp; enum action what = LIST; - char *type, *diskdevice, *device, *progname; + char *type, *diskdevice, *device; int verbose = 0; char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; - char * loopdev = NULL; - char * delim = NULL; - char *uuid = NULL; - char *mapname = NULL; - int hotplug = 0; + char *loopdev __attribute__((cleanup(cleanup_charp))) = NULL; + char *delim __attribute__((cleanup(cleanup_charp))) = NULL; + char *uuid __attribute__((cleanup(cleanup_charp))) = NULL; + char *_mapname __attribute__((cleanup(cleanup_charp))) = NULL; + char *mapname; int loopcreated = 0; struct stat buf; @@ -252,24 +207,7 @@ main(int argc, char **argv){ memset(&all, 0, sizeof(all)); memset(&partname, 0, sizeof(partname)); - /* Check whether hotplug mode. */ - progname = strrchr(argv[0], '/'); - - if (!progname) - progname = argv[0]; - else - progname++; - - if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */ - hotplug = 1; - - /* Setup for original kpartx variables */ - if (!(device = get_hotplug_device())) - exit(1); - - diskdevice = device; - what = ADD; - } else if (argc < 2) { + if (argc < 2) { usage(); exit(1); } @@ -292,7 +230,9 @@ main(int argc, char **argv){ verbose = 1; break; case 'p': - delim = optarg; + delim = strdup(optarg); + if (!delim) + exit(1); break; case 'l': what = LIST; @@ -329,12 +269,10 @@ main(int argc, char **argv){ exit(1); } - if (hotplug) { - /* already got [disk]device */ - } else if (optind == argc-2) { + if (optind == argc - 2) { device = argv[optind]; diskdevice = argv[optind+1]; - } else if (optind == argc-1) { + } else if (optind == argc - 1) { diskdevice = device = argv[optind]; } else { usage(); @@ -381,11 +319,15 @@ main(int argc, char **argv){ off = find_devname_offset(device); if (!loopdev) { - mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); - if (mapname) - uuid = dm_mapuuid(mapname); + _mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); + if (_mapname) + uuid = dm_mapuuid(_mapname); } + mapname = _mapname; + if (!mapname) + mapname = device + off; + /* * We are called for a non-DM device. * Make up a fake UUID for the device, unless "-d -f" is given. @@ -395,9 +337,6 @@ main(int argc, char **argv){ if (!uuid && !(what == DELETE && force_devmap)) uuid = nondm_create_uuid(buf.st_rdev); - if (!mapname) - mapname = device + off; - if (delim == NULL) { delim = xmalloc(DELIM_SIZE); memset(delim, 0, DELIM_SIZE); @@ -674,7 +613,6 @@ main(int argc, char **argv){ if (verbose) fprintf(stderr, "loop deleted : %s\n", device); } - end: dm_lib_exit(); diff --git a/libdmmp/Makefile b/libdmmp/Makefile index 7e0e25092..bd3b86bc9 100644 --- a/libdmmp/Makefile +++ b/libdmmp/Makefile @@ -14,7 +14,7 @@ HEADERS := libdmmp/libdmmp.h OBJS := libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o -CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKG_CONFIG) --cflags json-c) +CPPFLAGS += -I$(libdmmpdir) -I$(mpathutildir) -I$(mpathcmddir) $(shell $(PKG_CONFIG) --cflags json-c) CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden LIBDEPS += $(shell $(PKG_CONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread diff --git a/libdmmp/docs/man/dmmp_context_free.3 b/libdmmp/docs/man/dmmp_context_free.3 index 2a9bbb10f..ce74cfb00 100644 --- a/libdmmp/docs/man/dmmp_context_free.3 +++ b/libdmmp/docs/man/dmmp_context_free.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_free" 3 "dmmp_context_free" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_free" 3 "dmmp_context_free" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_free \- Release the memory of struct dmmp_context. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_func_set.3 b/libdmmp/docs/man/dmmp_context_log_func_set.3 index f593eb57b..05b1cd0f1 100644 --- a/libdmmp/docs/man/dmmp_context_log_func_set.3 +++ b/libdmmp/docs/man/dmmp_context_log_func_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_func_set \- Set log handler function. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_priority_get.3 b/libdmmp/docs/man/dmmp_context_log_priority_get.3 index 6f7c3204f..62f671ff2 100644 --- a/libdmmp/docs/man/dmmp_context_log_priority_get.3 +++ b/libdmmp/docs/man/dmmp_context_log_priority_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_priority_get \- Get log priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_log_priority_set.3 b/libdmmp/docs/man/dmmp_context_log_priority_set.3 index c1ff3715e..991d5bf92 100644 --- a/libdmmp/docs/man/dmmp_context_log_priority_set.3 +++ b/libdmmp/docs/man/dmmp_context_log_priority_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_log_priority_set \- Set log priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_new.3 b/libdmmp/docs/man/dmmp_context_new.3 index 487ccde8c..25ce6ad9c 100644 --- a/libdmmp/docs/man/dmmp_context_new.3 +++ b/libdmmp/docs/man/dmmp_context_new.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_new" 3 "dmmp_context_new" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_new" 3 "dmmp_context_new" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_new \- Create struct dmmp_context. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_timeout_get.3 b/libdmmp/docs/man/dmmp_context_timeout_get.3 index b738fb97b..317b3f4c8 100644 --- a/libdmmp/docs/man/dmmp_context_timeout_get.3 +++ b/libdmmp/docs/man/dmmp_context_timeout_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_timeout_get \- Get IPC timeout. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_timeout_set.3 b/libdmmp/docs/man/dmmp_context_timeout_set.3 index af84be94d..9db1d2591 100644 --- a/libdmmp/docs/man/dmmp_context_timeout_set.3 +++ b/libdmmp/docs/man/dmmp_context_timeout_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_timeout_set \- Set IPC timeout. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_userdata_get.3 b/libdmmp/docs/man/dmmp_context_userdata_get.3 index 23e1da41d..ef5755e8e 100644 --- a/libdmmp/docs/man/dmmp_context_userdata_get.3 +++ b/libdmmp/docs/man/dmmp_context_userdata_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_userdata_get \- Get user data pointer. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_context_userdata_set.3 b/libdmmp/docs/man/dmmp_context_userdata_set.3 index 8a4a76561..cd21644a3 100644 --- a/libdmmp/docs/man/dmmp_context_userdata_set.3 +++ b/libdmmp/docs/man/dmmp_context_userdata_set.3 @@ -1,4 +1,4 @@ -.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_context_userdata_set \- Set user data pointer. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_flush_mpath.3 b/libdmmp/docs/man/dmmp_flush_mpath.3 index e5690edb2..f8cb83e8d 100644 --- a/libdmmp/docs/man/dmmp_flush_mpath.3 +++ b/libdmmp/docs/man/dmmp_flush_mpath.3 @@ -1,4 +1,4 @@ -.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_flush_mpath \- Flush specified multipath device map if unused. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_last_error_msg.3 b/libdmmp/docs/man/dmmp_last_error_msg.3 index 548e1a75a..7aa980bdf 100644 --- a/libdmmp/docs/man/dmmp_last_error_msg.3 +++ b/libdmmp/docs/man/dmmp_last_error_msg.3 @@ -1,4 +1,4 @@ -.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_last_error_msg \- Retrieves the last error message. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_log_priority_str.3 b/libdmmp/docs/man/dmmp_log_priority_str.3 index cf3155642..1252e17c8 100644 --- a/libdmmp/docs/man/dmmp_log_priority_str.3 +++ b/libdmmp/docs/man/dmmp_log_priority_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_log_priority_str \- Convert log priority to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_array_free.3 b/libdmmp/docs/man/dmmp_mpath_array_free.3 index 8d2090e64..2a1f1aee8 100644 --- a/libdmmp/docs/man/dmmp_mpath_array_free.3 +++ b/libdmmp/docs/man/dmmp_mpath_array_free.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_array_free \- Free 'struct dmmp_mpath' pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_array_get.3 b/libdmmp/docs/man/dmmp_mpath_array_get.3 index 4191571ee..2bb7b186e 100644 --- a/libdmmp/docs/man/dmmp_mpath_array_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_array_get \- Query all existing multipath devices. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 b/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 index 261bea6a9..f368cc294 100644 --- a/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_kdev_name_get \- Retrieve kernel DEVNAME of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_name_get.3 b/libdmmp/docs/man/dmmp_mpath_name_get.3 index 894d318f8..20cc09b6d 100644 --- a/libdmmp/docs/man/dmmp_mpath_name_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_name_get \- Retrieve name(alias) of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_mpath_wwid_get.3 b/libdmmp/docs/man/dmmp_mpath_wwid_get.3 index e6afcb1a8..09c79aa6a 100644 --- a/libdmmp/docs/man/dmmp_mpath_wwid_get.3 +++ b/libdmmp/docs/man/dmmp_mpath_wwid_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_mpath_wwid_get \- Retrieve WWID of certain mpath. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_array_get.3 b/libdmmp/docs/man/dmmp_path_array_get.3 index de29c9d1e..7bd19e709 100644 --- a/libdmmp/docs/man/dmmp_path_array_get.3 +++ b/libdmmp/docs/man/dmmp_path_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_array_get \- Retrieve path pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_blk_name_get.3 b/libdmmp/docs/man/dmmp_path_blk_name_get.3 index 167e81c31..984ae9d4c 100644 --- a/libdmmp/docs/man/dmmp_path_blk_name_get.3 +++ b/libdmmp/docs/man/dmmp_path_blk_name_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_blk_name_get \- Retrieve block name. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_array_get.3 b/libdmmp/docs/man/dmmp_path_group_array_get.3 index b9b663f67..7faa89c90 100644 --- a/libdmmp/docs/man/dmmp_path_group_array_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_array_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_array_get \- Retrieve path groups pointer array. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_id_get.3 b/libdmmp/docs/man/dmmp_path_group_id_get.3 index 67ba5c00e..fb3cea644 100644 --- a/libdmmp/docs/man/dmmp_path_group_id_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_id_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_id_get \- Retrieve path group ID. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_priority_get.3 b/libdmmp/docs/man/dmmp_path_group_priority_get.3 index 09cbea9c5..ae8a08aa5 100644 --- a/libdmmp/docs/man/dmmp_path_group_priority_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_priority_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_priority_get \- Retrieve path group priority. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_selector_get.3 b/libdmmp/docs/man/dmmp_path_group_selector_get.3 index dc73fa82e..8fae07360 100644 --- a/libdmmp/docs/man/dmmp_path_group_selector_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_selector_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_selector_get \- Retrieve path group selector. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_status_get.3 b/libdmmp/docs/man/dmmp_path_group_status_get.3 index a7662b634..6adfeba65 100644 --- a/libdmmp/docs/man/dmmp_path_group_status_get.3 +++ b/libdmmp/docs/man/dmmp_path_group_status_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_status_get \- Retrieve path group status. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_group_status_str.3 b/libdmmp/docs/man/dmmp_path_group_status_str.3 index 160c8a7ed..a2d654348 100644 --- a/libdmmp/docs/man/dmmp_path_group_status_str.3 +++ b/libdmmp/docs/man/dmmp_path_group_status_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_group_status_str \- Convert path group status to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_status_get.3 b/libdmmp/docs/man/dmmp_path_status_get.3 index 92e64331b..33d103f70 100644 --- a/libdmmp/docs/man/dmmp_path_status_get.3 +++ b/libdmmp/docs/man/dmmp_path_status_get.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_status_get \- Retrieve the path status. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_path_status_str.3 b/libdmmp/docs/man/dmmp_path_status_str.3 index 456cf24e2..e69c8be69 100644 --- a/libdmmp/docs/man/dmmp_path_status_str.3 +++ b/libdmmp/docs/man/dmmp_path_status_str.3 @@ -1,4 +1,4 @@ -.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_path_status_str \- Convert path status to string. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_reconfig.3 b/libdmmp/docs/man/dmmp_reconfig.3 index 413a3e6fc..b17c2d689 100644 --- a/libdmmp/docs/man/dmmp_reconfig.3 +++ b/libdmmp/docs/man/dmmp_reconfig.3 @@ -1,4 +1,4 @@ -.TH "dmmp_reconfig" 3 "dmmp_reconfig" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_reconfig" 3 "dmmp_reconfig" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_reconfig \- Instruct multipathd daemon to do reconfiguration. .SH SYNOPSIS diff --git a/libdmmp/docs/man/dmmp_strerror.3 b/libdmmp/docs/man/dmmp_strerror.3 index 202941c9c..c1f80d315 100644 --- a/libdmmp/docs/man/dmmp_strerror.3 +++ b/libdmmp/docs/man/dmmp_strerror.3 @@ -1,4 +1,4 @@ -.TH "dmmp_strerror" 3 "dmmp_strerror" "August 2024" "Device Mapper Multipath API - libdmmp Manual" +.TH "dmmp_strerror" 3 "dmmp_strerror" "June 2025" "Device Mapper Multipath API - libdmmp Manual" .SH NAME dmmp_strerror \- Convert error code to string. .SH SYNOPSIS diff --git a/libdmmp/libdmmp.c b/libdmmp/libdmmp.c index 79550f52b..badd5e77a 100644 --- a/libdmmp/libdmmp.c +++ b/libdmmp/libdmmp.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include #include diff --git a/libmpathcmd/libmpathcmd.version b/libmpathcmd/libmpathcmd.version index 81bdb2b7d..c0acb806d 100644 --- a/libmpathcmd/libmpathcmd.version +++ b/libmpathcmd/libmpathcmd.version @@ -1,6 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2020 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later * * libmpathcmd ABI * diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version index e486c5cdc..cc7de80c6 100644 --- a/libmpathpersist/libmpathpersist.version +++ b/libmpathpersist/libmpathpersist.version @@ -1,6 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2020 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later * * libmpathpersist ABI * diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index f5267ebf1..e786e9174 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -76,7 +76,7 @@ static vector pathvec; static void mpath_persistent_reserve_free_vecs__(vector curmp, vector pathvec) { - free_multipathvec(curmp, KEEP_PATHS); + free_multipathvec(curmp); free_pathvec(pathvec, FREE_PATHS); } diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h index 449e2825a..a4edb83be 100644 --- a/libmpathpersist/mpath_persist.h +++ b/libmpathpersist/mpath_persist.h @@ -113,7 +113,9 @@ struct prin_capdescr { uint16_t length; uint8_t flags[2]; - uint16_t pr_type_mask; + uint16_t pr_type_mask; /* The two bytes of the type mask are treated + as a single big-endian number. So the valid + type bits are 0xea01 */ uint16_t _reserved; }; diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c index ea8d65ea1..3091c5c22 100644 --- a/libmpathpersist/mpath_persist_int.c +++ b/libmpathpersist/mpath_persist_int.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include #include @@ -536,19 +536,19 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, } } for (i = 0; i < count; i++) { - if (thread[i].param.status != MPATH_PR_SKIP && - thread[i].param.status != MPATH_PR_THREAD_ERROR) { + if (thread[i].param.status == MPATH_PR_SKIP) + continue; + if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); if (rc) { condlog(3, "%s: failed to join thread while retrying %d", mpp->wwid, i); } - if (thread[i].param.status == - MPATH_PR_RETRYABLE_ERROR) - retryable_error = true; - else if (status == MPATH_PR_SUCCESS) - status = thread[i].param.status; } + if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR) + retryable_error = true; + else if (status == MPATH_PR_SUCCESS) + status = thread[i].param.status; } need_retry = false; } @@ -1001,12 +1001,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd, case MPATH_PROUT_REG_SA: case MPATH_PROUT_REG_IGN_SA: if (unregistering) - update_prflag(mpp->alias, 0); + update_prflag(mpp, 0); else - update_prflag(mpp->alias, 1); + update_prflag(mpp, 1); break; case MPATH_PROUT_CLEAR_SA: - update_prflag(mpp->alias, 0); + update_prflag(mpp, 0); if (mpp->prkey_source == PRKEY_SOURCE_FILE) update_prkey(mpp->alias, 0); break; diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c index 6eaec7cd5..41e5f5654 100644 --- a/libmpathpersist/mpath_pr_ioctl.c +++ b/libmpathpersist/mpath_pr_ioctl.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "mpath_pr_ioctl.h" #include "mpath_persist.h" #include "unaligned.h" diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index bd8ed2be9..514000de7 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include "debug.h" #include "mpath_cmd.h" @@ -20,8 +20,9 @@ #include "uxsock.h" #include "mpathpr.h" #include "structs.h" +#include "strbuf.h" -static char *do_pr(char *alias, char *str) +static char *do_pr(char *alias, const char *str) { int fd; char *reply; @@ -51,24 +52,26 @@ static char *do_pr(char *alias, char *str) return reply; } -static int do_update_pr(char *alias, char *cmd, char *key) +static int do_update_pr(char *alias, char *cmd, const char *data) { - char str[256]; + STRBUF_ON_STACK(buf); char *reply = NULL; int ret = -1; - if (key) - snprintf(str, sizeof(str), "%s map %s key %s", cmd, alias, key); + if (data) + print_strbuf(&buf, "%s map %s %s %s", cmd, alias, + strcmp(cmd, "setprkey") ? "pathlist" : "key", data); else - snprintf(str, sizeof(str), "%s map %s", cmd, alias); + print_strbuf(&buf, "%s map %s", cmd, alias); - reply = do_pr(alias, str); + reply = do_pr(alias, get_strbuf_str(&buf)); if (reply) { if (strncmp(reply, "ok", 2) == 0) ret = 0; else ret = -1; - condlog(ret ? 0 : 4, "%s: message=%s reply=%s", alias, str, reply); + condlog(ret ? 0 : 4, "%s: message=%s reply=%s", alias, + get_strbuf_str(&buf), reply); } free(reply); @@ -106,9 +109,31 @@ int get_prhold(char *mapname) return do_get_pr(mapname, "getprhold"); } -int update_prflag(char *mapname, int set) { - return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus", - NULL); +int update_prflag(struct multipath *mpp, int set) +{ + STRBUF_ON_STACK(buf); + int i, j; + bool first = true; + struct pathgroup *pgp = NULL; + struct path *pp = NULL; + + if (!set) + return do_update_pr(mpp->alias, "unsetprstatus", NULL); + + append_strbuf_str(&buf, "\""); + vector_foreach_slot (mpp->pg, pgp, j) { + vector_foreach_slot (pgp->paths, pp, i) { + if (pp->state == PATH_UP || pp->state == PATH_GHOST) { + if (first) { + append_strbuf_str(&buf, pp->dev); + first = false; + } else + print_strbuf(&buf, " %s", pp->dev_t); + } + } + } + append_strbuf_str(&buf, "\""); + return do_update_pr(mpp->alias, "setprstatus", get_strbuf_str(&buf)); } int update_prhold(char *mapname, bool set) diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h index 99d6b82ff..adbfc8653 100644 --- a/libmpathpersist/mpathpr.h +++ b/libmpathpersist/mpathpr.h @@ -1,12 +1,14 @@ #ifndef MPATHPR_H_INCLUDED #define MPATHPR_H_INCLUDED +#include "structs.h" + /* * This header file contains symbols that are only used by * libmpathpersist internally. */ -int update_prflag(char *mapname, int set); +int update_prflag(struct multipath *mpp, int set); int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags); int get_prflag(char *mapname); int get_prhold(char *mapname); diff --git a/libmpathutil/Makefile b/libmpathutil/Makefile index aebd8cce1..e1cc2c611 100644 --- a/libmpathutil/Makefile +++ b/libmpathutil/Makefile @@ -12,7 +12,7 @@ LIBDEPS += -lpthread -ldl -ludev -L$(mpathcmddir) -lmpathcmd $(SYSTEMD_LIBDEPS) # they need to be recompiled for unit tests # other object files -OBJS := parser.o vector.o util.o debug.o time-util.o \ +OBJS := mt-libudev.o parser.o vector.o util.o debug.o time-util.o \ uxsock.o log_pthread.o log.o strbuf.o globals.o msort.o all: $(DEVLIB) diff --git a/libmpathutil/globals.c b/libmpathutil/globals.c index 4d913496f..2596701ee 100644 --- a/libmpathutil/globals.c +++ b/libmpathutil/globals.c @@ -1,5 +1,5 @@ #include -#include +#include "mt-udev-wrap.h" #include "globals.h" __attribute__((weak)) struct config *get_multipath_config(void) diff --git a/libmpathutil/libmpathutil.version b/libmpathutil/libmpathutil.version index 14aa083e0..22ef99458 100644 --- a/libmpathutil/libmpathutil.version +++ b/libmpathutil/libmpathutil.version @@ -1,6 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2020 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later * * libmultipath ABI (libmpathutil part) * @@ -43,7 +43,7 @@ LIBMPATHCOMMON_1.0.0 { put_multipath_config; }; -LIBMPATHUTIL_4.0 { +LIBMPATHUTIL_5.0 { global: alloc_bitfield; alloc_strvec; @@ -93,6 +93,68 @@ global: log_thread_stop; logsink; msort; + + mt_udev_ref; + mt_udev_unref; + mt_udev_new; + + mt_udev_list_entry_get_next; + mt_udev_list_entry_get_by_name; + mt_udev_list_entry_get_name; + mt_udev_list_entry_get_value; + + mt_udev_device_ref; + mt_udev_device_unref; + + mt_udev_device_new_from_syspath; + mt_udev_device_new_from_devnum; + mt_udev_device_new_from_subsystem_sysname; + mt_udev_device_new_from_device_id; + mt_udev_device_new_from_environment; + + mt_udev_device_get_udev; + mt_udev_device_get_parent; + mt_udev_device_get_parent_with_subsystem_devtype; + mt_udev_device_get_devpath; + mt_udev_device_get_subsystem; + mt_udev_device_get_devtype; + mt_udev_device_get_syspath; + mt_udev_device_get_sysname; + mt_udev_device_get_devnum; + mt_udev_device_get_is_initialized; + mt_udev_device_get_property_value; + mt_udev_device_get_seqnum; + mt_udev_device_set_sysattr_value; + mt_udev_device_get_sysattr_value; + mt_udev_device_get_driver; + mt_udev_device_get_devnode; + + mt_udev_device_get_properties_list_entry; + + mt_udev_monitor_new_from_netlink; + mt_udev_monitor_enable_receiving; + mt_udev_monitor_get_fd; + mt_udev_monitor_receive_device; + mt_udev_monitor_filter_add_match_subsystem_devtype; + mt_udev_monitor_ref; + mt_udev_monitor_unref; + mt_udev_monitor_set_receive_buffer_size; + + mt_udev_enumerate_new; + mt_udev_enumerate_ref; + mt_udev_enumerate_unref; + mt_udev_enumerate_add_match_subsystem; + mt_udev_enumerate_scan_devices; + mt_udev_enumerate_get_list_entry; + mt_udev_enumerate_add_nomatch_subsystem; + mt_udev_enumerate_add_match_sysattr; + mt_udev_enumerate_add_nomatch_sysattr; + mt_udev_enumerate_add_match_property; + mt_udev_enumerate_add_match_tag; + mt_udev_enumerate_add_match_parent; + mt_udev_enumerate_add_match_is_initialized; + mt_udev_enumerate_add_syspath; + normalize_timespec; parse_devt; print_strbuf; diff --git a/libmpathutil/mt-libudev.c b/libmpathutil/mt-libudev.c new file mode 100644 index 000000000..2e37c7d9d --- /dev/null +++ b/libmpathutil/mt-libudev.c @@ -0,0 +1,776 @@ +#include "mt-libudev.h" +#include +#include +#include "util.h" + +static pthread_mutex_t libudev_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct udev *mt_udev_ref(struct udev *udev) +{ + struct udev *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_ref(udev); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev *mt_udev_unref(struct udev *udev) +{ + struct udev *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_unref(udev); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev *mt_udev_new(void) +{ + struct udev *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_new(); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry *mt_udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + struct udev_list_entry *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_list_entry_get_next(list_entry); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry * +mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) +{ + struct udev_list_entry *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_list_entry_get_by_name(list_entry, name); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_list_entry_get_name(list_entry); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_list_entry_get_value(list_entry); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device * +mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_new_from_syspath(udev, syspath); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device * +mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_new_from_devnum(udev, type, devnum); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device * +mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, + const char *sysname) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_new_from_subsystem_sysname(udev, subsystem, sysname); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_new_from_device_id(udev, id); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_new_from_environment(struct udev *udev) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_new_from_environment(udev); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_ref(struct udev_device *udev_device) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_ref(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_unref(struct udev_device *udev_device) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_unref(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev *mt_udev_device_get_udev(struct udev_device *udev_device) +{ + struct udev *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_udev(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_parent(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype( + struct udev_device *udev_device, const char *subsystem, const char *devtype) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_parent_with_subsystem_devtype(udev_device, + subsystem, devtype); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_devpath(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_devpath(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_subsystem(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_subsystem(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_devtype(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_devtype(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_syspath(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_syspath(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_sysname(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_sysname(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +dev_t mt_udev_device_get_devnum(struct udev_device *udev_device) +{ + dev_t ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_devnum(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +unsigned long long mt_udev_device_get_seqnum(struct udev_device *udev_device) +{ + unsigned long long ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_seqnum(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_driver(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_driver(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_devnode(struct udev_device *udev_device) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_devnode(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_device_get_is_initialized(struct udev_device *udev_device) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_is_initialized(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +const char * +mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_property_value(udev_device, key); + + pthread_cleanup_pop(1); + return ret; +} + +const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device, + const char *sysattr) +{ + const char *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_sysattr_value(udev_device, sysattr); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_device_set_sysattr_value(struct udev_device *udev_device, + const char *sysattr, char *value) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_set_sysattr_value(udev_device, sysattr, value); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry * +mt_udev_device_get_properties_list_entry(struct udev_device *udev_device) +{ + struct udev_list_entry *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_properties_list_entry(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +/* + * multipath-tools doesn't use these, and some of them aren't available + * in older libudev versions. Keeping the code here in case we will + * need it in the future. + +struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct +udev_device *udev_device) +{ + struct udev_list_entry *ret; + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_devlinks_list_entry(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry *mt_udev_device_get_tags_list_entry(struct udev_device +*udev_device) +{ + struct udev_list_entry *ret; + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_tags_list_entry(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct +udev_device *udev_device) +{ + struct udev_list_entry *ret; + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_current_tags_list_entry(udev_device); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry *mt_udev_device_get_sysattr_list_entry(struct +udev_device *udev_device) +{ + struct udev_list_entry *ret; + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_device_get_sysattr_list_entry(udev_device); + + pthread_cleanup_pop(1); + return ret; +} +*/ + +struct udev_monitor * +mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name) +{ + struct udev_monitor *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_new_from_netlink(udev, name); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + struct udev_monitor *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_ref(udev_monitor); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + struct udev_monitor *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_unref(udev_monitor); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_enable_receiving(udev_monitor); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_get_fd(udev_monitor); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_device *mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_device *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_receive_device(udev_monitor); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_monitor_filter_add_match_subsystem_devtype( + struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, + subsystem, devtype); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_monitor_set_receive_buffer_size(udev_monitor, size); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev) +{ + struct udev_enumerate *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_new(udev); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate) +{ + struct udev_enumerate *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_ref(udev_enumerate); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_enumerate *mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate) +{ + struct udev_enumerate *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_unref(udev_enumerate); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, + const char *subsystem) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_subsystem(udev_enumerate, subsystem); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, + const char *subsystem) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, + const char *sysattr, const char *value) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, + const char *sysattr, const char *value) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, + const char *property, const char *value) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_property(udev_enumerate, property, value); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, + const char *tag) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_tag(udev_enumerate, tag); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, + struct udev_device *parent) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_parent(udev_enumerate, parent); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_match_is_initialized(udev_enumerate); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, + const char *syspath) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_add_syspath(udev_enumerate, syspath); + + pthread_cleanup_pop(1); + return ret; +} + +int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) +{ + int ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_scan_devices(udev_enumerate); + + pthread_cleanup_pop(1); + return ret; +} + +struct udev_list_entry * +mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) +{ + struct udev_list_entry *ret; + + pthread_mutex_lock(&libudev_mutex); + pthread_cleanup_push(cleanup_mutex, &libudev_mutex); + + ret = udev_enumerate_get_list_entry(udev_enumerate); + + pthread_cleanup_pop(1); + return ret; +} diff --git a/libmpathutil/mt-libudev.h b/libmpathutil/mt-libudev.h new file mode 100644 index 000000000..1c1e748dc --- /dev/null +++ b/libmpathutil/mt-libudev.h @@ -0,0 +1,120 @@ +#ifndef MT_LIBUDEV_H +#define MT_LIBUDEV_H + +#include +#include /* for va_list */ +#include /* For dev_t */ + +struct udev; +struct udev_list_entry; +struct udev_device; +struct udev_monitor; +struct udev_enumerate; + +struct udev *mt_udev_ref(struct udev *udev); +struct udev *mt_udev_unref(struct udev *udev); +struct udev *mt_udev_new(void); + +struct udev_list_entry * +mt_udev_list_entry_get_next(struct udev_list_entry *list_entry); +struct udev_list_entry * +mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); +const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry); +const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry); + +struct udev_device * +mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath); +struct udev_device * +mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); +struct udev_device * +mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, + const char *sysname); +/* + * Some older libudev versions don't use "const" for the id argument, + * therefore we can't use it here, either. + */ +struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id); +struct udev_device *mt_udev_device_new_from_environment(struct udev *udev); + +struct udev_device *mt_udev_device_ref(struct udev_device *udev_device); +struct udev_device *mt_udev_device_unref(struct udev_device *udev_device); +struct udev *mt_udev_device_get_udev(struct udev_device *udev_device); +struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device); +struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype( + struct udev_device *udev_device, const char *subsystem, + const char *devtype); +const char *mt_udev_device_get_devpath(struct udev_device *udev_device); +const char *mt_udev_device_get_subsystem(struct udev_device *udev_device); +const char *mt_udev_device_get_devtype(struct udev_device *udev_device); +const char *mt_udev_device_get_syspath(struct udev_device *udev_device); +const char *mt_udev_device_get_sysname(struct udev_device *udev_device); +int mt_udev_device_get_is_initialized(struct udev_device *udev_device); +const char * +mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key); +dev_t mt_udev_device_get_devnum(struct udev_device *udev_device); +unsigned long long int mt_udev_device_get_seqnum(struct udev_device *udev_device); +const char *mt_udev_device_get_driver(struct udev_device *udev_device); +const char *mt_udev_device_get_devnode(struct udev_device *udev_device); +const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device, + const char *sysattr); +/* + * libudev-215 (Debian jessie) doesn't use "const" for the value argument, + * therefore we can't use it here, either. + */ +int mt_udev_device_set_sysattr_value(struct udev_device *udev_device, + const char *sysattr, char *value); + +struct udev_list_entry * +mt_udev_device_get_properties_list_entry(struct udev_device *udev_device); +/* + * multipath-tools doesn't use these, and some of them aren't available + * in older libudev versions. + + struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct + udev_device *udev_device); struct udev_list_entry + *mt_udev_device_get_tags_list_entry(struct udev_device *udev_device); struct + udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct udev_device + *udev_device); struct udev_list_entry + *mt_udev_device_get_sysattr_list_entry(struct udev_device *udev_device); +*/ +struct udev_monitor * +mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name); +struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor); +struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor); +int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); +int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor); +struct udev_device * +mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor); +int mt_udev_monitor_filter_add_match_subsystem_devtype( + struct udev_monitor *udev_monitor, const char *subsystem, + const char *devtype); +int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, + int size); + +struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev); +struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate); +struct udev_enumerate * +mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate); +int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, + const char *subsystem); +int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, + const char *subsystem); +int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, + const char *sysattr, const char *value); +int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, + const char *sysattr, const char *value); +int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, + const char *property, const char *value); +int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, + const char *tag); +int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, + struct udev_device *parent); +int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); +int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, + const char *syspath); +int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); +int mt_udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); +struct udev_list_entry * +mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); + +#endif diff --git a/libmpathutil/mt-udev-wrap.h b/libmpathutil/mt-udev-wrap.h new file mode 100644 index 000000000..f23282100 --- /dev/null +++ b/libmpathutil/mt-udev-wrap.h @@ -0,0 +1,90 @@ +#ifndef MT_LIBUDEV_WRAP_HPP +#define MT_LIBUDEV_WRAP_HPP +#include "mt-libudev.h" + +#define udev_ref(udev) mt_udev_ref(udev) +#define udev_unref(udev) mt_udev_unref(udev) +#define udev_new() mt_udev_new() + +#define udev_list_entry_get_next(list_entry) mt_udev_list_entry_get_next(list_entry) +#define udev_list_entry_get_by_name(list_entry, name) mt_udev_list_entry_get_by_name(list_entry, name) +#define udev_list_entry_get_name(list_entry) mt_udev_list_entry_get_name(list_entry) +#define udev_list_entry_get_value(list_entry) mt_udev_list_entry_get_value(list_entry) + +#define udev_list_entry_foreach(list_entry, first_entry) \ + for (list_entry = first_entry; \ + list_entry; \ + list_entry = udev_list_entry_get_next(list_entry)) + +#define udev_device_new_from_syspath(udev, syspath) mt_udev_device_new_from_syspath(udev, syspath) +#define udev_device_new_from_devnum(udev, type, devnum) mt_udev_device_new_from_devnum(udev, type, devnum) +#define udev_device_new_from_subsystem_sysname(udev, subsystem, sysname) \ + mt_udev_device_new_from_subsystem_sysname(udev, subsystem, sysname) +#define udev_device_new_from_device_id(udev, id) mt_udev_device_new_from_device_id(udev, id) +#define udev_device_new_from_environment(udev) mt_udev_device_new_from_environment(udev) + +#define udev_device_ref(udev_device) mt_udev_device_ref(udev_device) +#define udev_device_unref(udev_device) mt_udev_device_unref(udev_device) +#define udev_device_get_udev(udev_device) mt_udev_device_get_udev(udev_device) +#define udev_device_get_parent(udev_device) mt_udev_device_get_parent(udev_device) +#define udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype) \ + mt_udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype) +#define udev_device_get_devpath(udev_device) mt_udev_device_get_devpath(udev_device) +#define udev_device_get_subsystem(udev_device) mt_udev_device_get_subsystem(udev_device) +#define udev_device_get_devtype(udev_device) mt_udev_device_get_devtype(udev_device) +#define udev_device_get_syspath(udev_device) mt_udev_device_get_syspath(udev_device) +#define udev_device_get_sysname(udev_device) mt_udev_device_get_sysname(udev_device) +#define udev_device_get_is_initialized(udev_device) mt_udev_device_get_is_initialized(udev_device) +#define udev_device_get_property_value(udev_device, key) mt_udev_device_get_property_value(udev_device, key) +#define udev_device_get_devnum(udev_device) mt_udev_device_get_devnum(udev_device) +#define udev_device_get_seqnum(udev_device) mt_udev_device_get_seqnum(udev_device) +#define udev_device_get_driver(udev_device) mt_udev_device_get_driver(udev_device) +#define udev_device_get_devnode(udev_device) mt_udev_device_get_devnode(udev_device) +#define udev_device_get_sysattr_value(udev_device, sysattr) \ + mt_udev_device_get_sysattr_value(udev_device, sysattr) +#define udev_device_set_sysattr_value(udev_device, sysattr, value) \ + mt_udev_device_set_sysattr_value(udev_device, sysattr, value) + +#define udev_device_get_devlinks_list_entry(udev_device) mt_udev_device_get_devlinks_list_entry(udev_device) +#define udev_device_get_properties_list_entry(udev_device) mt_udev_device_get_properties_list_entry(udev_device) +#define udev_device_get_tags_list_entry(udev_device) mt_udev_device_get_tags_list_entry(udev_device) +#define udev_device_get_current_tags_list_entry(udev_device) mt_udev_device_get_current_tags_list_entry(udev_device) +#define udev_device_get_sysattr_list_entry(udev_device) mt_udev_device_get_sysattr_list_entry(udev_device) + +#define udev_monitor_new_from_netlink(udev, name) mt_udev_monitor_new_from_netlink(udev, name) +#define udev_monitor_ref(udev_monitor) mt_udev_monitor_ref(udev_monitor) +#define udev_monitor_unref(udev_monitor) mt_udev_monitor_unref(udev_monitor) +#define udev_monitor_enable_receiving(udev_monitor) mt_udev_monitor_enable_receiving(udev_monitor) +#define udev_monitor_get_fd(udev_monitor) mt_udev_monitor_get_fd(udev_monitor) +#define udev_monitor_receive_device(udev_monitor) mt_udev_monitor_receive_device(udev_monitor) +#define udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype) \ + mt_udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype) +#define udev_monitor_set_receive_buffer_size(udev_monitor, size) \ + mt_udev_monitor_set_receive_buffer_size(udev_monitor, size) + +#define udev_enumerate_new(udev) mt_udev_enumerate_new(udev) +#define udev_enumerate_unref(udev_enumerate) mt_udev_enumerate_unref(udev_enumerate) +#define udev_enumerate_add_match_subsystem(udev_enumerate, subsystem) \ + mt_udev_enumerate_add_match_subsystem(udev_enumerate, subsystem) +#define udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem) \ + mt_udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem) +#define udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value) \ + mt_udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value) +#define udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value) \ + mt_udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value) +#define udev_enumerate_add_match_property(udev_enumerate, property, value) \ + mt_udev_enumerate_add_match_property(udev_enumerate, property, value) +#define udev_enumerate_add_match_tag(udev_enumerate, tag) mt_udev_enumerate_add_match_tag(udev_enumerate, tag) +#define udev_enumerate_add_match_parent(udev_enumerate, parent) \ + mt_udev_enumerate_add_match_parent(udev_enumerate, parent) +#define udev_enumerate_add_match_is_initialized(udev_enumerate) \ + mt_udev_enumerate_add_match_is_initialized(udev_enumerate) +#define udev_enumerate_add_syspath(udev_enumerate, syspath) \ + mt_udev_enumerate_add_syspath(udev_enumerate, syspath) + +#define udev_enumerate_scan_devices(udev_enumerate) mt_udev_enumerate_scan_devices(udev_enumerate) +#define udev_enumerate_scan_device(udev_enumerate, syspath) \ + mt_udev_enumerate_scan_device(udev_enumerate, syspath) +#define udev_enumerate_get_list_entry(udev_enumerate) mt_udev_enumerate_get_list_entry(udev_enumerate) + +#endif diff --git a/libmpathutil/parser.c b/libmpathutil/parser.c index 131a5ed03..bf7c411b8 100644 --- a/libmpathutil/parser.c +++ b/libmpathutil/parser.c @@ -144,7 +144,7 @@ snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw, const void *data) { int r = 0; - char *f; + const char *f; struct config *conf; STRBUF_ON_STACK(sbuf); diff --git a/libmpathutil/strbuf.c b/libmpathutil/strbuf.c index 491a343aa..3ff1413bd 100644 --- a/libmpathutil/strbuf.c +++ b/libmpathutil/strbuf.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2021 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include diff --git a/libmpathutil/strbuf.h b/libmpathutil/strbuf.h index 790dc233c..44818431a 100644 --- a/libmpathutil/strbuf.h +++ b/libmpathutil/strbuf.h @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2021 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef STRBUF_H_INCLUDED #define STRBUF_H_INCLUDED diff --git a/libmpathutil/util.c b/libmpathutil/util.c index 38e984fc8..23a9797ce 100644 --- a/libmpathutil/util.c +++ b/libmpathutil/util.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "util.h" #include "debug.h" @@ -38,7 +38,7 @@ strchop(char *str) */ const char *libmp_basename(const char *filename) { - char *p = strrchr(filename, '/'); + const char *p = strrchr(filename, '/'); return p ? p + 1 : filename; } @@ -337,15 +337,15 @@ void cleanup_fclose(void *p) fclose(p); } -void cleanup_bitfield(struct bitfield **p) +void cleanup_bitfield(union bitfield **p) { free(*p); } -struct bitfield *alloc_bitfield(unsigned int maxbit) +union bitfield *alloc_bitfield(unsigned int maxbit) { unsigned int n; - struct bitfield *bf; + union bitfield *bf; if (maxbit == 0) { errno = EINVAL; @@ -353,7 +353,7 @@ struct bitfield *alloc_bitfield(unsigned int maxbit) } n = (maxbit - 1) / bits_per_slot + 1; - bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t)); + bf = calloc(1, sizeof(union bitfield) + (n - 1) * sizeof(bitfield_t)); if (bf) bf->len = maxbit; return bf; diff --git a/libmpathutil/util.h b/libmpathutil/util.h index 3799fd402..aed1bc1ee 100644 --- a/libmpathutil/util.h +++ b/libmpathutil/util.h @@ -10,12 +10,14 @@ #include #include #include -#include #ifndef __GLIBC_PREREQ #define __GLIBC_PREREQ(x, y) 0 #endif +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +struct udev_device; size_t strchop(char *); const char *libmp_basename(const char *filename); @@ -86,29 +88,40 @@ typedef unsigned int bitfield_t; #endif #define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT) -struct bitfield { - unsigned int len; - bitfield_t bits[]; +union bitfield { + struct { + unsigned int len; + bitfield_t bits[]; + }; + /* for initialization in the BITFIELD macro */ + struct { + unsigned int __len; + bitfield_t __bits[1]; + }; }; -#define BITFIELD(name, length) \ - struct { \ - unsigned int len; \ - bitfield_t bits[((length) - 1) / bits_per_slot + 1]; \ - } __storage_for__ ## name = { \ - .len = (length), \ - .bits = { 0, }, \ +/* + * BITFIELD: define a static bitfield of length bits_per_slot + * (aka 64 on 64-bit architectures). + * gcc 4.9.2 (Debian Jessie) raises an error if the initializer for + * .__len comes first. Thus put .__bits first. + * Use e.g. BUILD_BUG_ON() to make sure the bitfield size is sufficient + * to hold the number of bits required. + */ +#define BITFIELD(name) \ + union bitfield __storage_for__ ## name = { \ + .__bits = { 0 }, \ + .__len = (bits_per_slot), \ }; \ - struct bitfield *name = (struct bitfield *)& __storage_for__ ## name + union bitfield *name = & __storage_for__ ## name -struct bitfield *alloc_bitfield(unsigned int maxbit); +union bitfield *alloc_bitfield(unsigned int maxbit); void log_bitfield_overflow__(const char *f, unsigned int bit, unsigned int len); #define log_bitfield_overflow(bit, len) \ log_bitfield_overflow__(__func__, bit, len) -static inline bool is_bit_set_in_bitfield(unsigned int bit, - const struct bitfield *bf) +static inline bool is_bit_set_in_bitfield(unsigned int bit, const union bitfield *bf) { if (bit >= bf->len) { log_bitfield_overflow(bit, bf->len); @@ -118,7 +131,7 @@ static inline bool is_bit_set_in_bitfield(unsigned int bit, (1ULL << (bit % bits_per_slot))); } -static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf) +static inline void set_bit_in_bitfield(unsigned int bit, union bitfield *bf) { if (bit >= bf->len) { log_bitfield_overflow(bit, bf->len); @@ -127,7 +140,7 @@ static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf) bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot)); } -static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf) +static inline void clear_bit_in_bitfield(unsigned int bit, union bitfield *bf) { if (bit >= bf->len) { log_bitfield_overflow(bit, bf->len); @@ -146,5 +159,5 @@ static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf) void cleanup_charp(char **p); void cleanup_ucharp(unsigned char **p); void cleanup_udev_device(struct udev_device **udd); -void cleanup_bitfield(struct bitfield **p); +void cleanup_bitfield(union bitfield **p); #endif /* UTIL_H_INCLUDED */ diff --git a/libmpathutil/vector.c b/libmpathutil/vector.c index 3d54ed9e2..3e9f941be 100644 --- a/libmpathutil/vector.c +++ b/libmpathutil/vector.c @@ -81,8 +81,7 @@ vector_insert_slot(vector v, int slot, void *value) return v->slot[slot]; } -int -find_slot(vector v, void * addr) +int find_slot(vector v, const void *addr) { int i; diff --git a/libmpathutil/vector.h b/libmpathutil/vector.h index 07012776c..7b2c5597c 100644 --- a/libmpathutil/vector.h +++ b/libmpathutil/vector.h @@ -74,7 +74,7 @@ extern void free_strvec(vector strvec); extern void vector_set_slot(vector v, void *value); extern void vector_del_slot(vector v, int slot); extern void *vector_insert_slot(vector v, int slot, void *value); -int find_slot(vector v, void * addr); +int find_slot(vector v, const void *addr); int vector_find_or_add_slot(vector v, void *value); extern void vector_dump(vector v); extern void dump_strvec(vector strvec); diff --git a/libmpathvalid/mpath_valid.c b/libmpathvalid/mpath_valid.c index 7073d17d0..132578dca 100644 --- a/libmpathvalid/mpath_valid.c +++ b/libmpathvalid/mpath_valid.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include "devmapper.h" diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 17e1b54a3..bdade4cc9 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "checkers.h" #include "vector.h" diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index f32b58299..3a30d5699 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -1,7 +1,7 @@ #ifndef BLACKLIST_H_INCLUDED #define BLACKLIST_H_INCLUDED -#include +#include "mt-udev-wrap.h" #include #define MATCH_NOTHING 0 diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index e2eda58d0..bb6ad1ee6 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -43,6 +43,7 @@ static const char *checker_state_names[PATH_MAX_STATE] = { [PATH_TIMEOUT] = "timeout", [PATH_REMOVED] = "removed", [PATH_DELAYED] = "delayed", + [PATH_DISCONNECTED] = "disconnected", }; static LIST_HEAD(checkers); @@ -363,6 +364,7 @@ static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = { [CHECKER_MSGID_DOWN] = " reports path is down", [CHECKER_MSGID_GHOST] = " reports path is ghost", [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device", + [CHECKER_MSGID_DISCONNECTED] = " no access to this device", }; const char *checker_message(const struct checker *c) diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index da91f499e..a969e7d15 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -66,6 +66,15 @@ * delay_watch_checks checks, when it comes back up again, it will not * be marked as up until it has been up for delay_wait_checks checks. * During this time, it is marked as "delayed" + * + * PATH_DISCONNECTED is a special ephemeral state used to signal that a path + * has been disconnected at the storage target (e.g., LUN unmapped). When a + * checker returns PATH_DISCONNECTED: + * 1. The path's pp->disconnected field is set to track purge state + * 2. The state is immediately converted to PATH_DOWN for normal processing + * 3. If purge_disconnected is enabled, the path will be removed via sysfs + * This state should never be stored in pp->state or pp->chkrstate; it exists + * only as a transient return value from checkers to trigger special handling. */ enum path_check_state { PATH_WILD = 0, @@ -78,6 +87,7 @@ enum path_check_state { PATH_TIMEOUT, PATH_REMOVED, PATH_DELAYED, + PATH_DISCONNECTED, /* Ephemeral: mapped to PATH_DOWN */ PATH_MAX_STATE }; @@ -113,9 +123,10 @@ enum { CHECKER_MSGID_DOWN, CHECKER_MSGID_GHOST, CHECKER_MSGID_UNSUPPORTED, + CHECKER_MSGID_DISCONNECTED, CHECKER_GENERIC_MSGTABLE_SIZE, - CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */ - CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */ + CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */ + CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */ }; struct checker_class; diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index 0010acf8b..ba4ca683c 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -198,6 +198,16 @@ tur_check(int fd, unsigned int timeout, short *msgid) *msgid = MSG_TUR_TRANSITIONING; return PATH_PENDING; } + } else if (key == 0x5) { + /* Illegal request */ + if (asc == 0x25 && ascq == 0x00) { + /* + * LUN NOT SUPPORTED: unmapped at target. + * Signals pp->disconnected, becomes PATH_DOWN. + */ + *msgid = CHECKER_MSGID_DISCONNECTED; + return PATH_DISCONNECTED; + } } *msgid = CHECKER_MSGID_DOWN; return PATH_DOWN; diff --git a/libmultipath/config.c b/libmultipath/config.c index 8b424d18f..f9198d605 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -5,7 +5,7 @@ */ #include #include -#include +#include "mt-udev-wrap.h" #include #include #include @@ -168,13 +168,19 @@ hwe_regmatch (const struct hwentry *hwe1, const char *vendor, return retval; } +static const char *avoid_null(const char *s) +{ + return s ? s : "(unset)"; +} + static void _log_match(const char *fn, const struct hwentry *h, const char *vendor, const char *product, const char *revision) { condlog(4, "%s: found match /%s:%s:%s/ for '%s:%s:%s'", fn, - h->vendor, h->product, h->revision, - vendor, product, revision); + avoid_null(h->vendor), avoid_null(h->product), + avoid_null(h->revision), avoid_null(vendor), + avoid_null(product), avoid_null(revision)); } #define log_match(h, v, p, r) _log_match(__func__, (h), (v), (p), (r)) @@ -203,7 +209,8 @@ find_hwe (const struct vector_s *hwtable, log_match(tmp, vendor, product, revision); } condlog(n > 1 ? 3 : 4, "%s: found %d hwtable matches for %s:%s:%s", - __func__, n, vendor, product, revision); + __func__, n, avoid_null(vendor), avoid_null(product), + avoid_null(revision)); return n; } @@ -470,6 +477,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) merge_num(marginal_path_err_rate_threshold); merge_num(marginal_path_err_recheck_gap_time); merge_num(marginal_path_double_failed_time); + merge_num(purge_disconnected); snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product); reconcile_features_with_options(id, &dst->features, @@ -517,6 +525,7 @@ merge_mpe(struct mpentry *dst, struct mpentry *src) merge_num(skip_kpartx); merge_num(max_sectors_kb); merge_num(ghost_delay); + merge_num(purge_disconnected); merge_num(uid); merge_num(gid); merge_num(mode); diff --git a/libmultipath/config.h b/libmultipath/config.h index 5b4ebf8ce..618fa5723 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -88,6 +88,7 @@ struct hwentry { int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; + int purge_disconnected; int skip_kpartx; int max_sectors_kb; int ghost_delay; @@ -130,6 +131,7 @@ struct mpentry { int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; + int purge_disconnected; int skip_kpartx; int max_sectors_kb; int ghost_delay; @@ -188,6 +190,7 @@ struct config { int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; + int purge_disconnected; int uxsock_timeout; int strict_timing; int retrigger_tries; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index baa135739..0bcec0890 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "mpath_cmd.h" #include "checkers.h" @@ -355,6 +355,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs) select_max_sectors_kb(conf, mpp); select_ghost_delay(conf, mpp); select_flush_on_last_del(conf, mpp); + select_purge_disconnected(conf, mpp); sysfs_set_scsi_tmo(conf, mpp); marginal_pathgroups = conf->marginal_pathgroups; @@ -375,7 +376,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs) */ if (mpp->pg) { vector_foreach_slot (mpp->pg, pgp, i) - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); vector_free(mpp->pg); mpp->pg = NULL; @@ -439,8 +440,8 @@ static int pgcmp(struct multipath *mpp, struct multipath *cmpp) { int i, j; struct pathgroup *pgp, *cpgp; - BITFIELD(bf, bits_per_slot); - struct bitfield *bf__ __attribute__((cleanup(cleanup_bitfield))) = NULL; + BITFIELD(bf); + union bitfield *bf__ __attribute__((cleanup(cleanup_bitfield))) = NULL; if (VECTOR_SIZE(mpp->pg) != VECTOR_SIZE(cmpp->pg)) return 1; @@ -1049,7 +1050,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, vector newmp; struct config *conf = NULL; int allow_queueing; - struct bitfield *size_mismatch_seen; + union bitfield *size_mismatch_seen; struct multipath * cmpp; /* ignore refwwid if it's empty */ @@ -1140,7 +1141,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, if (!mpp->paths) { condlog(0, "%s: skip coalesce (no paths)", mpp->alias); - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); continue; } @@ -1172,7 +1173,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, if (cmd == CMD_DRY_RUN && mpp->action == ACT_UNDEF) mpp->action = ACT_DRY_RUN; if (setup_map(mpp, ¶ms, vecs)) { - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); continue; } @@ -1192,7 +1193,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, condlog(2, "%s: %s map", mpp->alias, (mpp->action == ACT_CREATE)? "ignoring" : "removing"); - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); continue; } else /* if (r == DOMAP_RETRY && !is_daemon) */ { ret = CP_RETRY; @@ -1201,7 +1202,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, } if (r == DOMAP_DRY) { if (!vector_alloc_slot(newmp)) { - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); goto out; } vector_set_slot(newmp, mpp); @@ -1224,20 +1225,20 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, if (mpp->action != ACT_REJECT) { if (!vector_alloc_slot(newmp)) { - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); goto out; } vector_set_slot(newmp, mpp); } else - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); } ret = CP_OK; out: free(size_mismatch_seen); if (!mpvec) { vector_foreach_slot (newmp, mpp, i) - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); vector_free(newmp); } return ret; diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 134b690a6..d80dd7d44 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -58,6 +58,7 @@ #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF #define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF #define DEFAULT_AUTO_RESIZE AUTO_RESIZE_NEVER +#define DEFAULT_PURGE_DISCONNECTED PURGE_DISCONNECTED_OFF /* Enable no foreign libraries by default */ #define DEFAULT_ENABLE_FOREIGN "NONE" diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 8e632ba80..e3870f597 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -1279,7 +1279,7 @@ int dm_get_maps(vector mp) switch (dm_get_multipath(names->name, &mpp)) { case DMP_OK: if (!vector_alloc_slot(mp)) { - free_multipath(mpp, KEEP_PATHS); + free_multipath(mpp); return 1; } vector_set_slot(mp, mpp); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index a06a6138a..797707d9d 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -23,7 +23,7 @@ #include "util.h" #include #include -#include +#include "mt-udev-wrap.h" #include "autoconfig.h" #include "mpath_cmd.h" #include "dict.h" @@ -941,6 +941,16 @@ declare_hw_snprint(skip_kpartx, print_yes_no_undef) declare_mp_handler(skip_kpartx, set_yes_no_undef) declare_mp_snprint(skip_kpartx, print_yes_no_undef) +declare_def_handler(purge_disconnected, set_yes_no_undef) +declare_def_snprint_defint(purge_disconnected, print_yes_no_undef, + DEFAULT_PURGE_DISCONNECTED) +declare_ovr_handler(purge_disconnected, set_yes_no_undef) +declare_ovr_snprint(purge_disconnected, print_yes_no_undef) +declare_hw_handler(purge_disconnected, set_yes_no_undef) +declare_hw_snprint(purge_disconnected, print_yes_no_undef) +declare_mp_handler(purge_disconnected, set_yes_no_undef) +declare_mp_snprint(purge_disconnected, print_yes_no_undef) + declare_def_range_handler(remove_retries, 0, INT_MAX) declare_def_snprint(remove_retries, print_int) @@ -2227,6 +2237,7 @@ init_keywords(vector keywords) install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay); install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); install_keyword("skip_kpartx", &def_skip_kpartx_handler, &snprint_def_skip_kpartx); + install_keyword("purge_disconnected", &def_purge_disconnected_handler, &snprint_def_purge_disconnected); install_keyword("disable_changed_wwids", &deprecated_disable_changed_wwids_handler, &snprint_deprecated); install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); @@ -2310,6 +2321,7 @@ init_keywords(vector keywords) install_keyword("marginal_path_err_recheck_gap_time", &hw_marginal_path_err_recheck_gap_time_handler, &snprint_hw_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &hw_marginal_path_double_failed_time_handler, &snprint_hw_marginal_path_double_failed_time); install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx); + install_keyword("purge_disconnected", &hw_purge_disconnected_handler, &snprint_hw_purge_disconnected); install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb); install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay); install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt); @@ -2355,6 +2367,7 @@ init_keywords(vector keywords) install_keyword("marginal_path_double_failed_time", &ovr_marginal_path_double_failed_time_handler, &snprint_ovr_marginal_path_double_failed_time); install_keyword("skip_kpartx", &ovr_skip_kpartx_handler, &snprint_ovr_skip_kpartx); + install_keyword("purge_disconnected", &ovr_purge_disconnected_handler, &snprint_ovr_purge_disconnected); install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb); install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay); install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt); @@ -2400,6 +2413,7 @@ init_keywords(vector keywords) install_keyword("marginal_path_err_recheck_gap_time", &mp_marginal_path_err_recheck_gap_time_handler, &snprint_mp_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &mp_marginal_path_double_failed_time_handler, &snprint_mp_marginal_path_double_failed_time); install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx); + install_keyword("purge_disconnected", &mp_purge_disconnected_handler, &snprint_mp_purge_disconnected); install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb); install_keyword("ghost_delay", &mp_ghost_delay_handler, &snprint_mp_ghost_delay); install_sublevel_end(); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 31db8758b..0c5e5ca69 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -8,13 +8,15 @@ #include #include #include +#include +#include #include #include #include #include #include -#include +#include "mt-udev-wrap.h" #include "checkers.h" #include "vector.h" #include "util.h" @@ -325,19 +327,23 @@ static int sysfs_get_tgt_nodename(struct path *pp, char *node) { const char *tgtname, *value; - struct udev_device *parent, *tgtdev; + struct udev_device *parent, *tgtdev, *device, *target; int host, channel, tgtid = -1; if (!pp->udev) return 1; - parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, - "scsi", "scsi_device"); - if (!parent) + device = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", + "scsi_device"); + if (!device) return 1; + target = udev_device_get_parent_with_subsystem_devtype(device, "scsi", + "scsi_target"); /* Check for SAS */ - value = udev_device_get_sysattr_value(parent, "sas_address"); + if (!target) + return 1; + value = udev_device_get_sysattr_value(device, "sas_address"); if (value) { - tgtdev = udev_device_get_parent(parent); + tgtdev = udev_device_get_parent(target); while (tgtdev) { char c; @@ -362,7 +368,7 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) } /* Check for USB */ - tgtdev = udev_device_get_parent(parent); + tgtdev = target; while (tgtdev) { value = udev_device_get_subsystem(tgtdev); if (value && !strcmp(value, "usb")) { @@ -375,11 +381,8 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) } tgtdev = udev_device_get_parent(tgtdev); } - parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target"); - if (!parent) - return 1; /* Check for FibreChannel */ - tgtdev = udev_device_get_parent(parent); + tgtdev = udev_device_get_parent(target); value = udev_device_get_sysname(tgtdev); if (value && sscanf(value, "rport-%d:%d-%d", &host, &channel, &tgtid) == 3) { @@ -399,13 +402,15 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) strlcpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; - } else + } else { udev_device_unref(tgtdev); + return 1; + } } } /* Check for iSCSI */ - parent = pp->udev; + parent = target; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); @@ -428,13 +433,14 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) strlcpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; - } - else + } else { udev_device_unref(tgtdev); + return 1; + } } } /* Check for libata */ - parent = pp->udev; + parent = target; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); @@ -876,10 +882,15 @@ sysfs_set_nexus_loss_tmo(struct path *pp) static void scsi_tmo_error_msg(struct path *pp) { - static BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1); + static BITFIELD(bf); STRBUF_ON_STACK(proto_buf); unsigned int proto_id = bus_protocol_id(pp); + /* make sure the bitfield is large enough */ + BUILD_BUG_ON((LAST_BUS_PROTOCOL_ID + 1) > bits_per_slot); + + if (is_bit_set_in_bitfield(proto_id, bf)) + return; snprint_path_protocol(&proto_buf, pp); condlog(2, "%s: setting scsi timeouts is unsupported for protocol %s", pp->dev, get_strbuf_str(&proto_buf)); @@ -2482,8 +2493,25 @@ int pathinfo(struct path *pp, struct config *conf, int mask) pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) pp->chkrstate = pp->state = newstate; + /* + * PATH_TIMEOUT and PATH_DISCONNECTED are ephemeral + * states that should never be stored in pp->state. + * Convert them to PATH_DOWN immediately. + */ if (pp->state == PATH_TIMEOUT) pp->state = PATH_DOWN; + if (pp->state == PATH_DISCONNECTED) { + int purge_enabled = pp->mpp && + pp->mpp->purge_disconnected == + PURGE_DISCONNECTED_ON; + if (purge_enabled && + pp->disconnected == NOT_DISCONNECTED) { + condlog(2, "%s: mark path for purge", + pp->dev); + pp->disconnected = DISCONNECTED_READY_FOR_PURGE; + } + pp->state = PATH_DOWN; + } if (pp->state == PATH_UP && !pp->size) { condlog(3, "%s: device size is 0, " "path unusable", pp->dev); diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index c8c47e0b4..3411bd00b 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -205,7 +205,7 @@ int disassemble_map(const struct vector_s *pathvec, return 1; } } else { - free_pgvec(mpp->pg, KEEP_PATHS); + free_pgvec(mpp->pg); mpp->pg = NULL; } @@ -261,7 +261,7 @@ int disassemble_map(const struct vector_s *pathvec, goto out; if (add_pathgroup(mpp, pgp)) { - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); goto out; } @@ -336,7 +336,7 @@ int disassemble_map(const struct vector_s *pathvec, out1: free(word); out: - free_pgvec(mpp->pg, KEEP_PATHS); + free_pgvec(mpp->pg); mpp->pg = NULL; return 1; } diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index 6f7ccb493..ad063ea58 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include "vector.h" #include "debug.h" diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h index 97c1f8ac5..db545e3b5 100644 --- a/libmultipath/foreign.h +++ b/libmultipath/foreign.h @@ -6,7 +6,7 @@ #define FOREIGN_H_INCLUDED #include -#include +#include "mt-udev-wrap.h" #define LIBMP_FOREIGN_API ((1 << 8) | 2) struct strbuf; diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c index 4cbdf60ce..04fa1e0f0 100644 --- a/libmultipath/foreign/nvme.c +++ b/libmultipath/foreign/nvme.c @@ -6,7 +6,7 @@ #include "nvme-lib.h" #include #include -#include +#include "mt-udev-wrap.h" #include #include #include diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 925230422..23c549335 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -682,7 +682,7 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, }, { - // Storwize V5000/V7000 lines / SAN Volume Controller (SVC) + // Storwize V3x00/V5000/V7000 lines / SAN Volume Controller (SVC) // Flex System V7000 / FlashSystem V840/V9000 and 5x00/7x00/9x00/Cx00 .vendor = "IBM", .product = "^2145", @@ -1326,6 +1326,15 @@ static struct hwentry default_hw[] = { .prio_name = PRIO_ALUA, .no_path_retry = 30, }, + { + // Exos / Nytro series + .vendor = "SEAGATE", + .product = "^[456]", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .prio_name = PRIO_ALUA, + .no_path_retry = 30, + }, /* * AccelStor */ diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c index 64054c181..acb087b78 100644 --- a/libmultipath/io_err_stat.c +++ b/libmultipath/io_err_stat.c @@ -397,6 +397,7 @@ static void account_async_io_state(struct io_err_stat_path *pp, int rc) switch (rc) { case PATH_DOWN: case PATH_TIMEOUT: + case PATH_DISCONNECTED: pp->io_err_nr++; break; case PATH_UNCHECKED: diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version index 89ae2a3c5..78fa2d436 100644 --- a/libmultipath/libmultipath.version +++ b/libmultipath/libmultipath.version @@ -1,6 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2020 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-or-later * * libmultipath ABI * @@ -43,7 +43,7 @@ LIBMPATHCOMMON_1.0.0 { put_multipath_config; }; -LIBMULTIPATH_30.0.0 { +LIBMULTIPATH_32.0.0 { global: /* symbols referenced by multipath and multipathd */ add_foreign; @@ -68,7 +68,8 @@ global: check_foreign; cleanup_bindings; cleanup_lock; - cleanup_multipath_and_paths; + cleanup_multipath; + cleanup_pathvec_and_free_paths; cleanup_udev_device_ptr; cleanup_udev_enumerate_ptr; coalesce_paths; @@ -85,7 +86,6 @@ global: dm_geteventnr; dm_get_major_minor; dm_get_maps; - dm_get_multipath; dm_is_mpath; dm_mapname; dm_prereq; @@ -159,6 +159,7 @@ global: remove_map; remove_map_by_alias; remove_map_callback; + remove_map_from_mpvec; remove_maps; remove_wwid; replace_wwids; @@ -242,8 +243,6 @@ global: sysfs_attr_get_value; sysfs_get_asymmetric_access_state; - /* foreign */ - local: *; }; diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index 23ef2bdc2..92f84c74d 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -158,8 +158,6 @@ int group_paths(struct multipath *mp, int marginal_pathgroups) } sort_pathgroups(mp); out: - vector_free(mp->paths); - mp->paths = NULL; return 0; fail_marginal: vector_free(normal); @@ -201,7 +199,7 @@ int group_by_match(struct multipath * mp, vector paths, bool (*path_match_fn)(struct path *, struct path *)) { int i, j; - struct bitfield *bitmap; + union bitfield *bitmap; struct path * pp; struct pathgroup * pgp; struct path * pp2; @@ -252,11 +250,11 @@ int group_by_match(struct multipath * mp, vector paths, free(bitmap); return 0; out2: - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); out1: free(bitmap); out: - free_pgvec(mp->pg, KEEP_PATHS); + free_pgvec(mp->pg); mp->pg = NULL; return 1; } @@ -314,9 +312,9 @@ int one_path_per_group(struct multipath *mp, vector paths) } return 0; out1: - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); out: - free_pgvec(mp->pg, KEEP_PATHS); + free_pgvec(mp->pg); mp->pg = NULL; return 1; } @@ -343,9 +341,9 @@ int one_group(struct multipath *mp, vector paths) /* aka multibus */ } return 0; out1: - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); out: - free_pgvec(mp->pg, KEEP_PATHS); + free_pgvec(mp->pg); mp->pg = NULL; return 1; } diff --git a/libmultipath/print.c b/libmultipath/print.c index 0d44a5a9f..02d6842d8 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "checkers.h" #include "vector.h" @@ -541,6 +541,8 @@ snprint_chk_state (struct strbuf *buff, const struct path * pp) return append_strbuf_str(buff, "i/o timeout"); case PATH_DELAYED: return append_strbuf_str(buff, "delayed"); + case PATH_DISCONNECTED: + return append_strbuf_str(buff, "disconnected"); default: return append_strbuf_str(buff, "undef"); } diff --git a/libmultipath/prio.c b/libmultipath/prio.c index a54487f3d..24f825bd8 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "debug.h" #include "util.h" diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c index e53f44751..053cccb72 100644 --- a/libmultipath/prioritizers/alua_rtpg.c +++ b/libmultipath/prioritizers/alua_rtpg.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c index 34527b224..ac2090ddd 100644 --- a/libmultipath/prioritizers/ana.c +++ b/libmultipath/prioritizers/ana.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "debug.h" #include "nvme-lib.h" diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c index 4fbde5ad2..b84746c9e 100644 --- a/libmultipath/prkey.c +++ b/libmultipath/prkey.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" /* MPATH_F_APTPL_MASK is publicly defined in mpath_persist.h */ #include <../libmpathpersist/mpath_persist.h> @@ -50,7 +50,7 @@ static int parse_prkey(const char *ptr, uint64_t *prkey) return 0; } -int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags) +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags) { char *flagstr; diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h index a89a617bd..806795ed3 100644 --- a/libmultipath/prkey.h +++ b/libmultipath/prkey.h @@ -15,7 +15,7 @@ int print_reservation_key(struct strbuf *buff, struct be64 key, uint8_t flags, int source); -int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags); +int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags); int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey, uint8_t sa_flags); int get_prkey(struct multipath *mpp, uint64_t *prkey, uint8_t *sa_flags); diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 4c0fbcf3f..56895c4bb 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -26,7 +26,7 @@ #include "propsel.h" #include "strbuf.h" #include -#include +#include "mt-udev-wrap.h" #include pgpolicyfn *pgpolicies[] = { @@ -1371,6 +1371,22 @@ int select_skip_kpartx (struct config *conf, struct multipath * mp) return 0; } +int select_purge_disconnected(struct config *conf, struct multipath *mp) +{ + const char *origin; + + mp_set_mpe(purge_disconnected); + mp_set_ovr(purge_disconnected); + mp_set_hwe(purge_disconnected); + mp_set_conf(purge_disconnected); + mp_set_default(purge_disconnected, DEFAULT_PURGE_DISCONNECTED); +out: + condlog(3, "%s: purge_disconnected = %s %s", mp->alias, + (mp->purge_disconnected == PURGE_DISCONNECTED_ON) ? "yes" : "no", + origin); + return 0; +} + int select_max_sectors_kb(struct config *conf, struct multipath * mp) { const char *origin; diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 559300506..bd20e39e4 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -39,6 +39,7 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp); int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp); int select_ghost_delay(struct config *conf, struct multipath * mp); +int select_purge_disconnected(struct config *conf, struct multipath *mp); void reconcile_features_with_options(const char *id, char **features, int* no_path_retry, int *retain_hwhandler); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index dfa547bb3..f44131272 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include "checkers.h" @@ -150,6 +150,7 @@ uninitialize_path(struct path *pp) pp->uid_attribute = NULL; pp->checker_timeout = 0; pp->pending_ticks = 0; + pp->pgindex = 0; if (checker_selected(&pp->checker)) checker_put(&pp->checker); @@ -169,6 +170,9 @@ free_path (struct path * pp) if (!pp) return; + if (pp->mpp) + condlog(0, "%s: INTERNAL ERROR: path %s references a map", + __func__, pp->dev_t); uninitialize_path(pp); if (pp->udev) { @@ -199,6 +203,11 @@ free_pathvec (vector vec, enum free_path_mode free_paths) vector_free(vec); } +void cleanup_pathvec_and_free_paths(vector *vec) +{ + free_pathvec(*vec, FREE_PATHS); +} + struct pathgroup * alloc_pathgroup (void) { @@ -220,18 +229,16 @@ alloc_pathgroup (void) return pgp; } -void -free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths) +void free_pathgroup(struct pathgroup *pgp) { if (!pgp) return; - free_pathvec(pgp->paths, free_paths); + free_pathvec(pgp->paths, KEEP_PATHS); free(pgp); } -void -free_pgvec (vector pgvec, enum free_path_mode free_paths) +void free_pgvec(vector pgvec) { int i; struct pathgroup * pgp; @@ -239,18 +246,8 @@ free_pgvec (vector pgvec, enum free_path_mode free_paths) if (!pgvec) return; - vector_foreach_slot(pgvec, pgp, i) { - - /* paths are going to be re-grouped, reset pgindex */ - if (free_paths != FREE_PATHS) { - struct path *pp; - int j; - - vector_foreach_slot(pgp->paths, pp, j) - pp->pgindex = 0; - } - free_pathgroup(pgp, free_paths); - } + vector_foreach_slot (pgvec, pgp, i) + free_pathgroup(pgp); vector_free(pgvec); } @@ -303,9 +300,12 @@ void free_multipath_attributes(struct multipath *mpp) } } -void -free_multipath (struct multipath * mpp, enum free_path_mode free_paths) +void free_multipath(struct multipath *mpp) { + struct pathgroup *pg; + struct path *pp; + int i, j; + if (!mpp) return; @@ -315,24 +315,17 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths) free(mpp->alias); mpp->alias = NULL; } - - if (!free_paths && mpp->pg) { - struct pathgroup *pgp; - struct path *pp; - int i, j; - - /* - * Make sure paths carry no reference to this mpp any more - */ - vector_foreach_slot(mpp->pg, pgp, i) { - vector_foreach_slot(pgp->paths, pp, j) - if (pp->mpp == mpp) - pp->mpp = NULL; - } - } - - free_pathvec(mpp->paths, free_paths); - free_pgvec(mpp->pg, free_paths); + vector_foreach_slot (mpp->pg, pg, i) { + vector_foreach_slot (pg->paths, pp, j) + if (pp->mpp == mpp) + pp->mpp = NULL; + } + vector_foreach_slot (mpp->paths, pp, i) + if (pp->mpp == mpp) + pp->mpp = NULL; + free_pathvec(mpp->paths, KEEP_PATHS); + free_pgvec(mpp->pg); + mpp->paths = mpp->pg = NULL; if (mpp->hwe) { vector_free(mpp->hwe); mpp->hwe = NULL; @@ -344,35 +337,10 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths) void cleanup_multipath(struct multipath **pmpp) { if (*pmpp) - free_multipath(*pmpp, KEEP_PATHS); + free_multipath(*pmpp); } -void cleanup_multipath_and_paths(struct multipath **pmpp) -{ - if (*pmpp) - free_multipath(*pmpp, FREE_PATHS); -} - -void -drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths) -{ - int i; - struct multipath * mpp; - - if (!mpvec) - return; - - vector_foreach_slot (mpvec, mpp, i) { - if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) { - free_multipath(mpp, free_paths); - vector_del_slot(mpvec, i); - return; - } - } -} - -void -free_multipathvec (vector mpvec, enum free_path_mode free_paths) +void free_multipathvec(vector mpvec) { int i; struct multipath * mpp; @@ -381,7 +349,7 @@ free_multipathvec (vector mpvec, enum free_path_mode free_paths) return; vector_foreach_slot (mpvec, mpp, i) - free_multipath(mpp, free_paths); + free_multipath(mpp); vector_free(mpvec); } diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 9247e74fc..9adedde25 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -186,6 +186,29 @@ enum auto_resize_state { AUTO_RESIZE_GROW_SHRINK, }; +/* + * purge_disconnected configuration option (per multipath device) + * Controls whether paths that become disconnected at the storage target + * should be automatically removed from the system via sysfs. + */ +enum purge_disconnected_states { + PURGE_DISCONNECTED_UNDEF = YNU_UNDEF, + PURGE_DISCONNECTED_OFF = YNU_NO, /* Don't purge */ + PURGE_DISCONNECTED_ON = YNU_YES, /* Purge disconnected paths */ +}; + +/* + * Path disconnection state (per path) + * Tracks whether a path has been marked for purge and whether it's already queued. + */ +enum path_disconnected_state { + NOT_DISCONNECTED, /* Path is not disconnected */ + DISCONNECTED_READY_FOR_PURGE, /* Path is disconnected and ready to be + queued for purge */ + DISCONNECTED_QUEUED_FOR_PURGE, /* Path is disconnected and already + queued for purge */ +}; + #define PROTOCOL_UNSET -1 enum scsi_protocol { @@ -382,6 +405,8 @@ struct path { int dmstate; int chkrstate; int oldstate; + enum path_disconnected_state disconnected; /* Marked for purge due to + disconnection */ int failcount; int priority; int pgindex; @@ -483,6 +508,7 @@ struct multipath { int ghost_delay; int ghost_delay_tick; int queue_mode; + int purge_disconnected; unsigned int sync_tick; int checker_count; enum prio_update_type prio_update; @@ -577,14 +603,13 @@ void *set_mpp_hwe(struct multipath *mpp, const struct path *pp); void uninitialize_path(struct path *pp); void free_path (struct path *); void free_pathvec (vector vec, enum free_path_mode free_paths); -void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths); -void free_pgvec (vector pgvec, enum free_path_mode free_paths); -void free_multipath (struct multipath *, enum free_path_mode free_paths); +void cleanup_pathvec_and_free_paths(vector *vec); +void free_pathgroup(struct pathgroup *pgp); +void free_pgvec(vector pgvec); +void free_multipath(struct multipath *mpp); void cleanup_multipath(struct multipath **pmpp); -void cleanup_multipath_and_paths(struct multipath **pmpp); -void free_multipath_attributes (struct multipath *); -void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths); -void free_multipathvec (vector mpvec, enum free_path_mode free_paths); +void free_multipath_attributes(struct multipath *); +void free_multipathvec(vector mpvec); struct adapter_group * alloc_adaptergroup(void); struct host_group * alloc_hostgroup(void); diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index f651b2915..9384de017 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "util.h" #include "checkers.h" @@ -181,6 +181,7 @@ static void update_pathvec_from_dm(vector pathvec, struct multipath *mpp, condlog(2, "%s: discarding non-existing path %s", mpp->alias, pp->dev_t); vector_del_slot(pgp->paths, j--); + pp->mpp = NULL; free_path(pp); must_reload = true; continue; @@ -201,6 +202,7 @@ static void update_pathvec_from_dm(vector pathvec, struct multipath *mpp, condlog(1, "%s: error %d in pathinfo, discarding path", pp->dev, rc); vector_del_slot(pgp->paths, j--); + pp->mpp = NULL; free_path(pp); must_reload = true; continue; @@ -254,7 +256,7 @@ static void update_pathvec_from_dm(vector pathvec, struct multipath *mpp, delete_pg: condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i); vector_del_slot(mpp->pg, i--); - free_pathgroup(pgp, KEEP_PATHS); + free_pathgroup(pgp); must_reload = true; /* Invalidate pgindex for all other pathgroups */ pg_deleted = true; @@ -369,33 +371,28 @@ int adopt_paths(vector pathvec, struct multipath *mpp, return 1; } -void orphan_path(struct path *pp, const char *reason) +static void orphan_path__(struct path *pp, const char *reason) { condlog(3, "%s: orphan path, %s", pp->dev, reason); - pp->mpp = NULL; - pp->pgindex = 0; uninitialize_path(pp); } +void orphan_path(struct path *pp, const char *reason) +{ + pp->mpp = NULL; + orphan_path__(pp, reason); +} + static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason) { int i; struct path * pp; vector_foreach_slot (pathvec, pp, i) { - if (pp->mpp == mpp) { - if (pp->initialized == INIT_REMOVED || - pp->initialized == INIT_PARTIAL) { - condlog(3, "%s: freeing path in %s state", - pp->dev, - pp->initialized == INIT_REMOVED ? - "removed" : "partial"); - vector_del_slot(pathvec, i--); - free_path(pp); - } else - orphan_path(pp, reason); - } else if (pp->add_when_online && - strncmp(mpp->wwid, pp->wwid, WWID_SIZE) == 0) { + if (pp->mpp == mpp) + orphan_path(pp, reason); + else if (pp->add_when_online && + strncmp(mpp->wwid, pp->wwid, WWID_SIZE) == 0) { pp->add_when_online = false; } } @@ -403,16 +400,13 @@ static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reas void set_path_removed(struct path *pp) { - struct multipath *mpp = pp->mpp; - - orphan_path(pp, "removed"); /* * Keep link to mpp. It will be removed when the path * is successfully removed from the map. */ - if (!mpp) + if (!pp->mpp) condlog(0, "%s: internal error: mpp == NULL", pp->dev); - pp->mpp = mpp; + orphan_path__(pp, "removed"); pp->initialized = INIT_REMOVED; } @@ -420,30 +414,26 @@ void remove_map_callback(struct multipath *mpp __attribute__((unused))) { } -void -remove_map(struct multipath *mpp, vector pathvec, vector mpvec) +void remove_map_from_mpvec(const struct multipath *mpp, vector mpvec) { - int i; + int i = find_slot(mpvec, mpp); - remove_map_callback(mpp); + if (i != -1) + vector_del_slot(mpvec, i); +} - free_pathvec(mpp->paths, KEEP_PATHS); - free_pgvec(mpp->pg, KEEP_PATHS); - mpp->paths = mpp->pg = NULL; +void remove_map(struct multipath *mpp, vector pathvec) +{ + remove_map_callback(mpp); /* - * clear references to this map + * clear references to this map. + * This needs to be called before free_multipath(), + * because of the add_when_online logic. */ orphan_paths(pathvec, mpp, "map removed internally"); - if (mpvec && - (i = find_slot(mpvec, (void *)mpp)) != -1) - vector_del_slot(mpvec, i); - - /* - * final free - */ - free_multipath(mpp, KEEP_PATHS); + free_multipath(mpp); } void @@ -452,7 +442,8 @@ remove_map_by_alias(const char *alias, struct vectors * vecs) struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { condlog(2, "%s: removing map by alias", alias); - remove_map(mpp, vecs->pathvec, vecs->mpvec); + remove_map_from_mpvec(mpp, vecs->mpvec); + remove_map(mpp, vecs->pathvec); } } @@ -466,7 +457,7 @@ remove_maps(struct vectors * vecs) return; vector_foreach_slot (vecs->mpvec, mpp, i) - remove_map(mpp, vecs->pathvec, NULL); + remove_map(mpp, vecs->pathvec); vector_free(vecs->mpvec); vecs->mpvec = NULL; @@ -573,6 +564,34 @@ static struct path *find_devt_in_pathgroups(const struct multipath *mpp, return NULL; } +/* + * check_removed_paths() + * + * This function removes paths from the pathvec and frees them if they have + * been marked for removal (INIT_REMOVED, INIT_PARTIAL). + * This is important because some callers (e.g. uev_add_path->ev_remove_path()) + * rely on the paths being actually gone when the call stack returns. + * Be sure not to call it while these paths are still referenced elsewhere + * (e.g. from coalesce_paths(), where curmp may still reference them). + * + * Most important call stacks in multipath-tools 0.13.0: + * + * checker_finished() + * sync_mpp() + * do_sync_mpp() + * update_multipath_strings() + * sync_paths() + * check_removed_paths() + * + * [multiple callers including update_map(), ev_remove_path(), ...] + * setup_multipath() + * refresh_multipath() + * update_multipath_strings() + * sync_paths() + * check_removed_paths() + * + * refresh_multipath() is also called from a couple of CLI handlers. + */ static void check_removed_paths(const struct multipath *mpp, vector pathvec) { struct path *pp; @@ -588,11 +607,13 @@ static void check_removed_paths(const struct multipath *mpp, vector pathvec) pp->initialized == INIT_REMOVED ? "removed" : "partial"); vector_del_slot(pathvec, i--); + pp->mpp = NULL; free_path(pp); } } } +/* This function may free paths. See check_removed_paths(). */ void sync_paths(struct multipath *mpp, vector pathvec) { struct path *pp; @@ -618,11 +639,15 @@ void sync_paths(struct multipath *mpp, vector pathvec) check_removed_paths(mpp, pathvec); update_mpp_paths(mpp, pathvec); vector_foreach_slot (mpp->paths, pp, i) - pp->mpp = mpp; + if (pp->mpp != mpp) { + condlog(2, "%s: %s: mpp of %s was %p, fixing to %p", + __func__, mpp->alias, pp->dev_t, pp->mpp, mpp); + pp->mpp = mpp; + }; } -int -update_multipath_strings(struct multipath *mpp, vector pathvec) +/* This function may free paths. See check_removed_paths(). */ +int update_multipath_strings(struct multipath *mpp, vector pathvec) { struct pathgroup *pgp; int i, r = DMP_ERR; @@ -634,7 +659,7 @@ update_multipath_strings(struct multipath *mpp, vector pathvec) condlog(4, "%s: %s", mpp->alias, __FUNCTION__); free_multipath_attributes(mpp); - free_pgvec(mpp->pg, KEEP_PATHS); + free_pgvec(mpp->pg); mpp->pg = NULL; r = update_multipath_table(mpp, pathvec, 0); @@ -832,7 +857,8 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp, return mpp; out: - remove_map(mpp, vecs->pathvec, vecs->mpvec); + remove_map_from_mpvec(mpp, vecs->mpvec); + remove_map(mpp, vecs->pathvec); return NULL; } diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 1188adac8..1eedfc85d 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -23,7 +23,8 @@ int update_mpp_paths(struct multipath * mpp, vector pathvec); int update_multipath_strings (struct multipath *mpp, vector pathvec); void extract_hwe_from_path(struct multipath * mpp); -void remove_map (struct multipath *mpp, vector pathvec, vector mpvec); +void remove_map_from_mpvec(const struct multipath *mpp, vector mpvec); +void remove_map(struct multipath *mpp, vector pathvec); void remove_map_by_alias(const char *alias, struct vectors * vecs); void remove_maps (struct vectors * vecs); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index af27d107b..24269813b 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 63cf2fbc0..be199af00 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include "autoconfig.h" #include "debug.h" diff --git a/libmultipath/valid.c b/libmultipath/valid.c index 7bf861606..1bf19364b 100644 --- a/libmultipath/valid.c +++ b/libmultipath/valid.c @@ -4,7 +4,7 @@ */ #include #include -#include +#include "mt-udev-wrap.h" #include #include diff --git a/libmultipath/version.h b/libmultipath/version.h index e8f9be00b..445ba9093 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -11,9 +11,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000D00 +#define VERSION_CODE 0x000E00 /* MMDDYY, in hex */ -#define DATE_CODE 0x0A0A19 +#define DATE_CODE 0x01101A #define PROG "multipath-tools" diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 97fd5a43f..f8c5f8ec7 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -8,7 +8,7 @@ #include "config.h" #include "structs.h" #include -#include +#include "mt-udev-wrap.h" #include "mpath_persist.h" #include "mpath_persist_int.h" #include "main.h" @@ -748,25 +748,42 @@ void mpath_print_buf_readcap( struct prin_resp *pr_buff) printf("Report capabilities response:\n"); - printf(" Compatible Reservation Handling(CRH): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); - printf(" Specify Initiator Ports Capable(SIP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); - printf(" All Target Ports Capable(ATP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4 )); - printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0])); - printf(" Type Mask Valid(TMV): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); - printf(" Allow Commands: %d\n", !!(( pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); + printf(" Compatible Reservation Handling(CRH): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); + printf(" Specify Initiator Ports Capable(SIP_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); + printf(" All Target Ports Capable(ATP_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4)); + printf(" Persist Through Power Loss Capable(PTPL_C): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x1)); + printf(" Type Mask Valid(TMV): %d\n", + !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); + printf(" Allow Commands: %d\n", + !!((pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); printf(" Persist Through Power Loss Active(PTPL_A): %d\n", - !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); + !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); if(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80) { printf(" Support indicated in Type mask:\n"); - printf(" %s: %d\n", pr_type_strs[7], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x80); - printf(" %s: %d\n", pr_type_strs[6], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x40); - printf(" %s: %d\n", pr_type_strs[5], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x20); - printf(" %s: %d\n", pr_type_strs[3], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x8); - printf(" %s: %d\n", pr_type_strs[1], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x2); - printf(" %s: %d\n", pr_type_strs[8], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x100); + printf(" %s: %d\n", pr_type_strs[7], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x8000)); + printf(" %s: %d\n", pr_type_strs[6], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x4000)); + printf(" %s: %d\n", pr_type_strs[5], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x2000)); + printf(" %s: %d\n", pr_type_strs[3], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x800)); + printf(" %s: %d\n", pr_type_strs[1], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & + 0x200)); + printf(" %s: %d\n", pr_type_strs[8], + !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x1)); } } diff --git a/multipath/main.c b/multipath/main.c index f2adcdeb0..f20d701c3 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include @@ -181,15 +181,15 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) if (refwwid && strlen(refwwid) && strncmp(mpp->wwid, refwwid, WWID_SIZE)) { condlog(3, "skip map %s: out of scope", mpp->alias); - remove_map(mpp, pathvec, curmp); - i--; + vector_del_slot(curmp, i--); + remove_map(mpp, pathvec); continue; } if (update_multipath_table(mpp, pathvec, flags) != DMP_OK) { condlog(1, "error parsing map %s", mpp->wwid); - remove_map(mpp, pathvec, curmp); - i--; + vector_del_slot(curmp, i--); + remove_map(mpp, pathvec); continue; } @@ -214,12 +214,17 @@ static int check_usable_paths(struct config *conf, const char *devpath, enum devtypes dev_type) { struct udev_device __attribute__((cleanup(cleanup_udev_device))) *ud = NULL; - struct multipath __attribute__((cleanup(cleanup_multipath_and_paths))) *mpp = NULL; struct pathgroup *pg; struct path *pp; char __attribute__((cleanup(cleanup_charp))) *params = NULL; char __attribute__((cleanup(cleanup_charp))) *status = NULL; - vector __attribute((cleanup(cleanup_vector))) pathvec = NULL; + vector __attribute((cleanup(cleanup_pathvec_and_free_paths))) pathvec = NULL; + /* + * The sequence of variable definitions matters here, cleanup handlers + * are executed in reverse order of definition, and we access pp->mpp + * in free_multipath(). + */ + struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp = NULL; char uuid[DM_UUID_LEN]; dev_t devt; int r = 1, i, j; @@ -436,7 +441,7 @@ static bool released_to_systemd(void) static struct vectors vecs; static void cleanup_vecs(void) { - free_multipathvec(vecs.mpvec, KEEP_PATHS); + free_multipathvec(vecs.mpvec); free_pathvec(vecs.pathvec, FREE_PATHS); } @@ -580,9 +585,9 @@ configure (struct config *conf, enum mpath_cmds cmd, if (refwwid) free(refwwid); - free_multipathvec(curmp, KEEP_PATHS); + free_multipathvec(curmp); vecs.mpvec = NULL; - free_multipathvec(newmp, KEEP_PATHS); + free_multipathvec(newmp); free_pathvec(pathvec, FREE_PATHS); vecs.pathvec = NULL; @@ -1069,7 +1074,7 @@ main (int argc, char *argv[]) printf("successfully reset wwids\n"); vector_foreach_slot_backwards(curmp, mpp, i) { vector_del_slot(curmp, i); - free_multipath(mpp, KEEP_PATHS); + free_multipath(mpp); } vector_free(curmp); goto out; diff --git a/multipath/multipath.conf.5.in b/multipath/multipath.conf.5.in index 3c9ae0977..84cd1a0ab 100644 --- a/multipath/multipath.conf.5.in +++ b/multipath/multipath.conf.5.in @@ -1306,6 +1306,22 @@ The default is: \fBno\fR . . .TP +.B purge_disconnected +If set to +.I yes +, multipathd will automatically remove devices that are in a disconnected state. +A path is considered disconnected when the TUR (Test Unit Ready) path checker +receives the SCSI sense code "LOGICAL UNIT NOT SUPPORTED" (sense key 0x5, +ASC/ASCQ 0x25/0x00). This typically indicates that the LUN has been unmapped +or is no longer presented by the storage array. This option helps clean up +stale device entries that would otherwise remain in the system. +.RS +.TP +The default is: \fBno\fR +.RE +. +. +.TP .B disable_changed_wwids (Deprecated) This option is not supported anymore, and will be ignored. .RE @@ -1602,6 +1618,8 @@ section: .TP .B skip_kpartx .TP +.B purge_disconnected +.TP .B max_sectors_kb .TP .B ghost_delay @@ -1797,6 +1815,8 @@ section: .TP .B skip_kpartx .TP +.B purge_disconnected +.TP .B max_sectors_kb .TP .B ghost_delay @@ -1881,6 +1901,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections: .TP .B skip_kpartx .TP +.B purge_disconnected +.TP .B max_sectors_kb .TP .B ghost_delay diff --git a/multipathd/Makefile b/multipathd/Makefile index 4bcee6bdc..6969807da 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -36,7 +36,7 @@ endif CLI_OBJS := multipathc.o cli.o OBJS := main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \ - dmevents.o init_unwinder.o + dmevents.o init_unwinder.o purge.o ifeq ($(FPIN_SUPPORT),1) OBJS += fpin_handlers.o endif diff --git a/multipathd/callbacks.c b/multipathd/callbacks.c index b6b57f458..2027c3a66 100644 --- a/multipathd/callbacks.c +++ b/multipathd/callbacks.c @@ -59,6 +59,8 @@ void init_handler_callbacks(void) set_unlocked_handler_callback(VRB_SHUTDOWN, HANDLER(cli_shutdown)); set_handler_callback(VRB_GETPRSTATUS | Q1_MAP, HANDLER(cli_getprstatus)); set_handler_callback(VRB_SETPRSTATUS | Q1_MAP, HANDLER(cli_setprstatus)); + set_handler_callback(VRB_SETPRSTATUS | Q1_MAP | Q2_PATHLIST, + HANDLER(cli_setprstatus_list)); set_handler_callback(VRB_UNSETPRSTATUS | Q1_MAP, HANDLER(cli_unsetprstatus)); set_handler_callback(VRB_FORCEQ | Q1_DAEMON, HANDLER(cli_force_no_daemon_q)); set_handler_callback(VRB_RESTOREQ | Q1_DAEMON, HANDLER(cli_restore_no_daemon_q)); diff --git a/multipathd/cli.c b/multipathd/cli.c index d0e6cebc9..0d679c867 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -227,6 +227,7 @@ load_keys (void) r += add_key(keys, "getprhold", VRB_GETPRHOLD, 0); r += add_key(keys, "setprhold", VRB_SETPRHOLD, 0); r += add_key(keys, "unsetprhold", VRB_UNSETPRHOLD, 0); + r += add_key(keys, "pathlist", KEY_PATHLIST, 1); if (r) { free_keys(keys); diff --git a/multipathd/cli.h b/multipathd/cli.h index 5a943a455..3e607389a 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -62,6 +62,7 @@ enum { KEY_LOCAL = 81, KEY_GROUP = 82, KEY_KEY = 83, + KEY_PATHLIST = 84, }; /* @@ -94,6 +95,7 @@ enum { Q2_LOCAL = KEY_LOCAL << 16, Q2_GROUP = KEY_GROUP << 16, Q2_KEY = KEY_KEY << 16, + Q2_PATHLIST = KEY_PATHLIST << 16, /* byte 3: qualifier 3 */ Q3_FMT = KEY_FMT << 24, diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 7f572fb44..f5a13c468 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -19,7 +19,7 @@ #include "print.h" #include "sysfs.h" #include -#include +#include "mt-udev-wrap.h" #include #include "util.h" #include "prkey.h" @@ -31,6 +31,7 @@ #include "foreign.h" #include "strbuf.h" #include "cli_handlers.h" +#include static struct path * find_path_by_str(const struct vector_s *pathvec, const char *str, @@ -1276,8 +1277,8 @@ cli_getprstatus (void * v, struct strbuf *reply, void * data) return 0; } -static int -cli_setprstatus(void * v, struct strbuf *reply, void * data) +static int do_setprstatus(void *v, struct strbuf *reply, void *data, + const struct vector_s *registered_paths) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; @@ -1291,13 +1292,45 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data) if (mpp->prflag != PR_SET) { set_pr(mpp); - condlog(2, "%s: prflag set", param); + pr_register_active_paths(mpp, registered_paths); + if (mpp->prflag == PR_SET) + condlog(2, "%s: prflag set", param); + else + condlog(0, "%s: Failed to set prflag", param); } memset(&mpp->old_pr_key, 0, 8); return 0; } +static int cli_setprstatus(void *v, struct strbuf *reply, void *data) +{ + return do_setprstatus(v, reply, data, NULL); +} + +static int cli_setprstatus_list(void *v, struct strbuf *reply, void *data) +{ + int r; + struct vector_s registered_paths_vec = {.allocated = 0}; + vector registered_paths + __attribute__((cleanup(cleanup_reset_vec))) = ®istered_paths_vec; + char *ptr = get_keyparam(v, KEY_PATHLIST); + + while (isspace(*ptr)) + ptr++; + while (*ptr) { + if (!vector_alloc_slot(registered_paths)) + return -ENOMEM; + vector_set_slot(registered_paths, ptr); + while (*ptr && !isspace(*ptr)) + ptr++; + while (isspace(*ptr)) + *ptr++ = '\0'; + } + r = do_setprstatus(v, reply, data, registered_paths); + return r; +} + static int cli_unsetprstatus(void * v, struct strbuf *reply, void * data) { diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c index 2920469dc..92a61bb16 100644 --- a/multipathd/fpin_handlers.c +++ b/multipathd/fpin_handlers.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include diff --git a/multipathd/init_unwinder.c b/multipathd/init_unwinder.c index b1cd283d5..d39e4282f 100644 --- a/multipathd/init_unwinder.c +++ b/multipathd/init_unwinder.c @@ -34,5 +34,7 @@ int init_unwinder(void) pthread_mutex_unlock(&dummy_mtx); - return pthread_cancel(dummy); + rc = pthread_cancel(dummy); + pthread_join(dummy, NULL); + return rc; } diff --git a/multipathd/main.c b/multipathd/main.c index d11a8576c..61e0ea346 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include "mt-udev-wrap.h" #include #include "fpin.h" #ifdef USE_SYSTEMD @@ -83,6 +83,7 @@ #include "dmevents.h" #include "io_err_stat.h" #include "foreign.h" +#include "purge.h" #include "../third-party/valgrind/drd.h" #include "init_unwinder.h" @@ -90,7 +91,8 @@ #define MSG_SIZE 32 static unsigned int -mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed); +mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed, + unsigned int nr_keys_wanted); #define LOG_MSG(lvl, pp) \ do { \ @@ -137,11 +139,11 @@ static enum force_reload_types reconfigure_pending = FORCE_RELOAD_NONE; pid_t daemon_pid; static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t config_cond; -static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr, - fpin_thr, fpin_consumer_thr; -static bool check_thr_started, uevent_thr_started, uxlsnr_thr_started, - uevq_thr_started, dmevent_thr_started, fpin_thr_started, - fpin_consumer_thr_started; +static pthread_t check_thr, purge_thr, uevent_thr, uxlsnr_thr, uevq_thr, + dmevent_thr, fpin_thr, fpin_consumer_thr; +static bool check_thr_started, purge_thr_started, uevent_thr_started, + uxlsnr_thr_started, uevq_thr_started, dmevent_thr_started, + fpin_thr_started, fpin_consumer_thr_started; static int pid_fd = -1; static inline enum daemon_status get_running_state(bool *pending_reconfig) @@ -479,7 +481,8 @@ remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs) condlog(3, "%s: removing map from internal tables", mpp->alias); if (!poll_dmevents) stop_waiter_thread(mpp); - remove_map(mpp, vecs->pathvec, vecs->mpvec); + remove_map_from_mpvec(mpp, vecs->mpvec); + remove_map(mpp, vecs->pathvec); } static void @@ -501,6 +504,7 @@ remove_maps_and_stop_waiters(struct vectors *vecs) remove_maps(vecs); } +/* This function may free paths. See check_removed_paths(). */ int refresh_multipath(struct vectors *vecs, struct multipath *mpp) { if (update_multipath_strings(mpp, vecs->pathvec) != DMP_OK) { @@ -511,6 +515,7 @@ int refresh_multipath(struct vectors *vecs, struct multipath *mpp) return 0; } +/* This function may free paths. See check_removed_paths(). */ int setup_multipath(struct vectors *vecs, struct multipath *mpp) { if (refresh_multipath(vecs, mpp) != 0) @@ -629,19 +634,47 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { return true; } -static void -pr_register_active_paths(struct multipath *mpp) +/* + * If reg_paths in non-NULL, it is a vector of paths that libmpathpersist + * registered. If the number of registered keys is smaller than the number + * of registered paths, then likely a preempt that occurred while + * libmpathpersist was registering the key. As long as there are still some + * registered keys, treat the preempt as happening first, and make sure to + * register keys on all the paths. If the number of registered keys is at + * least as large as the number of registered paths, then no preempt happened, + * and multipathd does not need to re-register the paths that libmpathpersist + * handled + */ +void pr_register_active_paths(struct multipath *mpp, const struct vector_s *reg_paths) { - unsigned int i, j, nr_keys = 0; + unsigned int i, j, k, nr_keys = 0; + unsigned int wanted_nr = VECTOR_SIZE(reg_paths); struct path *pp; struct pathgroup *pgp; + char *pathname; vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (mpp->prflag == PR_UNSET) return; - if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST)) - nr_keys = mpath_pr_event_handle(pp, nr_keys); + if (pp->state != PATH_UP && pp->state != PATH_GHOST) + continue; + if (wanted_nr && nr_keys) { + vector_foreach_slot (reg_paths, pathname, k) { + if (strcmp(pp->dev_t, pathname) == 0) { + goto skip; + } + } + } + nr_keys = mpath_pr_event_handle(pp, nr_keys, wanted_nr); + if (nr_keys && nr_keys < wanted_nr) { + /* + * Incorrect number of registered keys. Need + * to register all devices + */ + wanted_nr = 0; + } + skip:; /* a statement must follow a label on pre C23 clang */ } } } @@ -672,8 +705,7 @@ handle_orphaned_offline_paths(vector offline_paths) pp->add_when_online = true; } -static void -cleanup_reset_vec(struct vector_s **v) +void cleanup_reset_vec(struct vector_s **v) { vector_reset(*v); } @@ -717,7 +749,8 @@ update_map (struct multipath *mpp, struct vectors *vecs, int new_map) fail: if (new_map && wait_for_events(mpp, vecs)) { condlog(0, "%s: failed to create new map", mpp->alias); - remove_map(mpp, vecs->pathvec, vecs->mpvec); + remove_map_from_mpvec(mpp, vecs->mpvec); + remove_map(mpp, vecs->pathvec); return 1; } @@ -729,7 +762,7 @@ update_map (struct multipath *mpp, struct vectors *vecs, int new_map) sync_map_state(mpp, false); - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); if (VECTOR_SIZE(offline_paths) != 0) handle_orphaned_offline_paths(offline_paths); @@ -741,8 +774,8 @@ update_map (struct multipath *mpp, struct vectors *vecs, int new_map) static int add_map_without_path (struct vectors *vecs, const char *alias) { - struct multipath __attribute__((cleanup(cleanup_multipath_and_paths))) - *mpp = alloc_multipath(); + struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp = + alloc_multipath(); char __attribute__((cleanup(cleanup_charp))) *params = NULL; char __attribute__((cleanup(cleanup_charp))) *status = NULL; struct config *conf; @@ -772,11 +805,11 @@ static int add_map_without_path (struct vectors *vecs, const char *alias) mpp->mpe = find_mpe(conf->mptable, mpp->wwid); put_multipath_config(conf); - if ((rc = update_multipath_table__(mpp, vecs->pathvec, 0, params, status)) != DMP_OK) + if (update_multipath_table__(mpp, vecs->pathvec, 0, params, status) != DMP_OK || + !vector_alloc_slot(vecs->mpvec)) return DMP_ERR; - if (!vector_alloc_slot(vecs->mpvec)) - return DMP_ERR; + /* Make sure mpp is not cleaned up on return */ vector_set_slot(vecs->mpvec, steal_ptr(mpp)); /* @@ -1319,7 +1352,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) verify_paths(mpp); mpp->action = ACT_RELOAD; prflag = mpp->prflag; - mpath_pr_event_handle(pp, 0); + mpath_pr_event_handle(pp, 0, 0); } else { if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) { orphan_path(pp, "only one path"); @@ -1403,7 +1436,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) if (retries >= 0) { if ((mpp->prflag == PR_SET && prflag != PR_SET) || start_waiter) - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); condlog(2, "%s [%s]: path added to devmap %s", pp->dev, pp->dev_t, mpp->alias); return 0; @@ -1411,7 +1444,8 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) goto fail; fail_map: - remove_map(mpp, vecs->pathvec, vecs->mpvec); + remove_map_from_mpvec(mpp, vecs->mpvec); + remove_map(mpp, vecs->pathvec); fail: orphan_path(pp, "failed to add path"); return 1; @@ -1809,8 +1843,8 @@ map_discovery (struct vectors * vecs) vector_foreach_slot (vecs->mpvec, mpp, i) if (update_multipath_table(mpp, vecs->pathvec, DI_DISCOVERY) != DMP_OK) { - remove_map(mpp, vecs->pathvec, vecs->mpvec); - i--; + vector_del_slot(vecs->mpvec, i--); + remove_map(mpp, vecs->pathvec); } return 0; @@ -2474,6 +2508,27 @@ get_new_state(struct path *pp) if (newstate == PATH_REMOVED) newstate = PATH_DOWN; + /* + * PATH_DISCONNECTED is an ephemeral state used to signal that a path + * has been disconnected at the storage target (LUN unmapped). We use + * it to set pp->disconnected for purge tracking, then immediately + * convert it to PATH_DOWN for normal path failure handling. + * + * This ensures PATH_DISCONNECTED never gets stored in pp->state or + * pp->chkrstate - it exists only as a transient signal from the + * checker to trigger special handling before becoming PATH_DOWN. + */ + if (newstate == PATH_DISCONNECTED) { + if (pp->mpp->purge_disconnected == PURGE_DISCONNECTED_ON && + pp->disconnected == NOT_DISCONNECTED) { + condlog(2, "%s: mark (%s) path for purge", pp->dev, + checker_state_name(newstate)); + pp->disconnected = DISCONNECTED_READY_FOR_PURGE; + } + /* Always convert to PATH_DOWN for normal processing */ + newstate = PATH_DOWN; + } + if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { condlog(2, "%s: unusable path (%s) - checker failed", pp->dev, checker_state_name(newstate)); @@ -2486,8 +2541,8 @@ get_new_state(struct path *pp) return newstate; } -static int -do_sync_mpp(struct vectors * vecs, struct multipath *mpp) +/* This function may free paths. See check_removed_paths(). */ +static int do_sync_mpp(struct vectors *vecs, struct multipath *mpp) { int i, ret; struct path *pp; @@ -2505,8 +2560,8 @@ do_sync_mpp(struct vectors * vecs, struct multipath *mpp) return ret; } -static int -sync_mpp(struct vectors * vecs, struct multipath *mpp, unsigned int ticks) +/* This function may free paths. See check_removed_paths(). */ +static int sync_mpp(struct vectors *vecs, struct multipath *mpp, unsigned int ticks) { if (mpp->sync_tick) mpp->sync_tick -= (mpp->sync_tick > ticks) ? ticks : @@ -2545,6 +2600,7 @@ update_path_state (struct vectors * vecs, struct path * pp) pp->tick = 1; return CHECK_PATH_SKIPPED; } + if (pp->recheck_wwid == RECHECK_WWID_ON && (newstate == PATH_UP || newstate == PATH_GHOST) && ((pp->state != PATH_UP && pp->state != PATH_GHOST) || @@ -2646,9 +2702,9 @@ update_path_state (struct vectors * vecs, struct path * pp) */ condlog(2, "%s: checking persistent " "reservation registration", pp->dev); - mpath_pr_event_handle(pp, 0); + mpath_pr_event_handle(pp, 0, 0); if (pp->mpp->prflag == PR_SET && prflag != PR_SET) - pr_register_active_paths(pp->mpp); + pr_register_active_paths(pp->mpp, NULL); } /* @@ -3052,12 +3108,31 @@ static void enable_pathgroups(struct multipath *mpp) } } -static void checker_finished(struct vectors *vecs, unsigned int ticks) +static void free_orphan_paths(vector pathvec) +{ + struct path *pp; + int i; + + vector_foreach_slot (pathvec, pp, i) { + if (!pp->mpp && (pp->initialized == INIT_REMOVED || + pp->initialized == INIT_PARTIAL)) { + condlog(2, "%s: freeing orphan %s in %s state", + __func__, pp->dev, + pp->initialized == INIT_REMOVED ? "removed" : "partial"); + vector_del_slot(pathvec, i--); + free_path(pp); + } + } +} + +static void checker_finished(struct vectors *vecs, unsigned int ticks, + struct list_head *purge_list) { struct multipath *mpp; bool uev_timed_out = false; int i; + free_orphan_paths(vecs->pathvec); vector_foreach_slot(vecs->mpvec, mpp, i) { bool inconsistent, prio_reload, failback_reload; bool uev_wait_reload, ghost_reload; @@ -3100,6 +3175,12 @@ static void checker_finished(struct vectors *vecs, unsigned int ticks) if (uev_timed_out && !need_to_delay_reconfig(vecs)) unblock_reconfigure(); partial_retrigger_tick(vecs->pathvec); + + /* + * Build purge list for disconnected paths. + * The caller will queue it after releasing vecs->lock. + */ + build_purge_list(vecs, purge_list); } static void * @@ -3125,6 +3206,13 @@ checkerloop (void *ap) int num_paths = 0, strict_timing; unsigned int ticks = 0; enum checker_state checker_state = CHECKER_STARTING; + LIST_HEAD(purge_list); + + /* + * Cleanup handler to free purge_list if thread is cancelled. + * This prevents memory leaks during shutdown. + */ + pthread_cleanup_push(cleanup_purge_list, &purge_list); if (set_config_state(DAEMON_RUNNING) != DAEMON_RUNNING) /* daemon shutdown */ @@ -3169,10 +3257,24 @@ checkerloop (void *ap) checker_state = update_paths(vecs, &num_paths, start_time.tv_sec); if (checker_state == CHECKER_FINISHED) - checker_finished(vecs, ticks); + checker_finished(vecs, ticks, &purge_list); lock_cleanup_pop(vecs->lock); } + /* + * Queue purge work for disconnected paths. + * This is done after releasing vecs->lock to avoid holding + * the lock while signaling the purge thread. + */ + if (!list_empty(&purge_list)) { + pthread_cleanup_push(cleanup_mutex, &purge_mutex); + pthread_mutex_lock(&purge_mutex); + pthread_testcancel(); + list_splice_tail_init(&purge_list, &purge_queue); + pthread_cond_signal(&purge_cond); + pthread_cleanup_pop(1); + } + get_monotonic_time(&end_time); timespecsub(&end_time, &start_time, &diff_time); if (num_paths) { @@ -3224,6 +3326,12 @@ checkerloop (void *ap) break; } } + + /* + * Pop cleanup handler. Execute it (arg=1) to free purge_list + * at the end of each iteration. + */ + pthread_cleanup_pop(1); } pthread_cleanup_pop(1); return NULL; @@ -3309,7 +3417,7 @@ configure (struct vectors * vecs, enum force_reload_types reload_type) vector_foreach_slot(mpvec, mpp, i){ if (remember_wwid(mpp->wwid) == 1) trigger_paths_udev_change(mpp, true); - pr_register_active_paths(mpp); + pr_register_active_paths(mpp, NULL); } /* @@ -3324,8 +3432,8 @@ configure (struct vectors * vecs, enum force_reload_types reload_type) */ vector_foreach_slot(vecs->mpvec, mpp, i) { if (wait_for_events(mpp, vecs)) { - remove_map(mpp, vecs->pathvec, vecs->mpvec); - i--; + vector_del_slot(vecs->mpvec, i--); + remove_map(mpp, vecs->pathvec); continue; } if (setup_multipath(vecs, mpp)) @@ -3669,6 +3777,8 @@ static void cleanup_threads(void) if (check_thr_started) pthread_cancel(check_thr); + if (purge_thr_started) + pthread_cancel(purge_thr); if (uevent_thr_started) pthread_cancel(uevent_thr); if (uxlsnr_thr_started) @@ -3685,6 +3795,8 @@ static void cleanup_threads(void) if (check_thr_started) pthread_join(check_thr, NULL); + if (purge_thr_started) + pthread_join(purge_thr, NULL); if (uevent_thr_started) pthread_join(uevent_thr, NULL); if (uxlsnr_thr_started) @@ -3739,18 +3851,21 @@ static struct call_rcu_data *mp_rcu_data; static void cleanup_rcu(void) { - pthread_t rcu_thread; - /* Wait for any pending RCU calls */ rcu_barrier(); if (mp_rcu_data != NULL) { +#if (URCU_VERSION < 0x000E00) + pthread_t rcu_thread; rcu_thread = get_call_rcu_thread(mp_rcu_data); +#endif /* detach this thread from the RCU thread */ set_thread_call_rcu_data(NULL); synchronize_rcu(); /* tell RCU thread to exit */ call_rcu_data_free(mp_rcu_data); +#if (URCU_VERSION < 0x000E00) pthread_join(rcu_thread, NULL); +#endif } rcu_unregister_thread(); } @@ -3790,7 +3905,7 @@ child (__attribute__((unused)) void *param) int rc; struct config *conf; char *envp; - enum daemon_status state; + enum daemon_status state = DAEMON_INIT; int exit_code = 1; int fpin_marginal_paths = 0; @@ -3937,6 +4052,11 @@ child (__attribute__((unused)) void *param) goto failed; } else check_thr_started = true; + if ((rc = pthread_create(&purge_thr, &misc_attr, purgeloop, vecs))) { + condlog(0, "failed to create purge loop thread: %d", rc); + goto failed; + } else + purge_thr_started = true; if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) { condlog(0, "failed to create uevent dispatcher: %d", rc); goto failed; @@ -4267,7 +4387,10 @@ void unset_pr(struct multipath *mpp) * * The number of found keys must be at least as large as *nr_keys, * and if MPATH_PR_SUCCESS is returned and mpp->prflag is PR_SET after - * the call, *nr_keys will be set to the number of found keys. + * the call, *nr_keys will be set to the number of found keys. Otherwise + * if mpp->prflag is PR_UNSET it will be set to 0. If MPATH_PR_SUCCESS + * is not returned and mpp->prflag is not PR_UNSET, nr_keys will not be + * changed. */ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys) { @@ -4277,8 +4400,10 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n bool was_set = (mpp->prflag == PR_SET); /* If pr is explicitly unset, it must be manually set */ - if (mpp->prflag == PR_UNSET) + if (mpp->prflag == PR_UNSET) { + *nr_keys = 0; return MPATH_PR_SUCCESS; + } if (!get_be64(mpp->reservation_key)) { /* Nothing to do. Assuming pr mgmt feature is disabled*/ @@ -4286,6 +4411,7 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias); + *nr_keys = 0; return MPATH_PR_SUCCESS; } @@ -4293,8 +4419,10 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0); if (ret != MPATH_PR_SUCCESS) { - if (ret == MPATH_PR_ILLEGAL_REQ) + if (ret == MPATH_PR_ILLEGAL_REQ) { unset_pr(mpp); + *nr_keys = 0; + } condlog(0, "%s : pr in read keys service action failed Error=%d", mpp->alias, ret); return ret; @@ -4335,22 +4463,32 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n condlog(was_set ? 1 : 3, "%s: %u keys found. needed %u. prflag unset.", mpp->alias, nr_found, *nr_keys); + *nr_keys = 0; } return MPATH_PR_SUCCESS; } /* - * This function is called with the number of registered keys that should be + * This function is called with two numbers + * + * nr_keys_needed: the number of registered keys that should be * seen for this device to know that the key has not been preempted while the * path was getting registered. If 0 is passed in, update_mpath_pr is called * before registering the key to figure out the number, assuming that at * least one key must exist. * + * nr_keys_wanted: Only used if nr_keys_needed is 0, so we don't know how + * many keys we currently have. If nr_keys_wanted in non-zero and the + * number of keys found by the initial call to update_map_pr() is at least + * as large as it, exit early, since we have all the keys we are expecting. + * * The function returns the number of keys that are registered or 0 if * it's unknown. */ -static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed) +static unsigned int +mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed, + unsigned int nr_keys_wanted) { struct multipath *mpp = pp->mpp; int ret; @@ -4366,6 +4504,8 @@ static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_ nr_keys_needed = 1; if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS) return 0; + if (nr_keys_wanted && nr_keys_wanted <= nr_keys_needed) + return nr_keys_needed; } check_prhold(mpp, pp); @@ -4400,7 +4540,7 @@ static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_ clear_reg ? "Clearing" : "Setting", pp->dev, ret); } else if (!clear_reg) { if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS) - return 0; + return nr_keys_needed; if (mpp->prflag != PR_SET) { memset(¶m, 0, sizeof(param)); clear_reg = true; diff --git a/multipathd/main.h b/multipathd/main.h index 29b57e3de..b66208ec5 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -54,4 +54,7 @@ int resize_map(struct multipath *mpp, unsigned long long size, struct vectors *vecs); void set_pr(struct multipath *mpp); void unset_pr(struct multipath *mpp); +void pr_register_active_paths(struct multipath *mpp, + const struct vector_s *registered_paths); +void cleanup_reset_vec(struct vector_s **v); #endif /* MAIN_H_INCLUDED */ diff --git a/multipathd/multipathc.c b/multipathd/multipathc.c index 38f2d6a33..cb3d6f873 100644 --- a/multipathd/multipathc.c +++ b/multipathd/multipathc.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0-or-later /* * Copyright (c) 2022 SUSE LLC - * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in index 8815e099f..38a243e3e 100644 --- a/multipathd/multipathd.8.in +++ b/multipathd/multipathd.8.in @@ -344,48 +344,54 @@ will not be disabled when the daemon stops. Restores configured queue_without_daemon mode. . .TP -.B map|multipath $map setprstatus +.B setprstatus map|multipath $map Enable persistent reservation management on $map. . .TP -.B map|multipath $map unsetprstatus +.B setprstatus map|multipath $map pathlist $pathlist +Enable persistent reservation management on $map, and notify multipathd of +the paths that have been registered, so it doesn't attempt to re-register +them. +. +.TP +.B unsetprstatus map|multipath $map Disable persistent reservation management on $map. . .TP -.B map|multipath $map getprstatus +.B getprstatus map|multipath $map Get the current persistent reservation management status of $map. . .TP -.B map|multipath $map getprkey +.B getprkey map|multipath $map Get the current persistent reservation key associated with $map. . .TP -.B map|multipath $map setprkey key $key +.B setprkey map|multipath $map key $key Set the persistent reservation key associated with $map to $key in the \fIprkeys_file\fR. This key will only be used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR. . .TP -.B map|multipath $map unsetprkey +.B unsetprkey map|multipath $map Remove the persistent reservation key associated with $map from the \fIprkeys_file\fR. This will only unset the key used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR. . .TP -.B path $path setmarginal +.B setmarginal path $path move $path to a marginal pathgroup. The path will remain in the marginal path group until \fIunsetmarginal\fR is called. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). . .TP -.B path $path unsetmarginal +.B unsetmarginal path $path return marginal path $path to its normal pathgroup. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). . .TP -.B map $map unsetmarginal +.B unsetmarginal map $map return all marginal paths in $map to their normal pathgroups. This command will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths detection method configured (see the multipath.conf man page for details). diff --git a/multipathd/purge.c b/multipathd/purge.c new file mode 100644 index 000000000..44f0c9055 --- /dev/null +++ b/multipathd/purge.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Brian Bunker + * Copyright (C) 2025 Krishna Kant + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vector.h" +#include "structs.h" +#include "structs_vec.h" +#include "debug.h" +#include "util.h" +#include "lock.h" +#include "sysfs.h" +#include "list.h" +#include "purge.h" + +pthread_mutex_t purge_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t purge_cond = PTHREAD_COND_INITIALIZER; +LIST_HEAD(purge_queue); + +/* + * Information needed to purge a path. We copy this data while holding + * vecs->lock, then release the lock before doing the actual sysfs write. + * This prevents blocking other operations while waiting for sysfs I/O. + * + * The udev device reference captures the sysfs path (including H:C:T:L). + * The duplicated fd prevents device name/number reuse: the kernel will not + * reuse the device's minor number (which maps to the device name) for a new + * device while we hold an open file descriptor, even if the original device + * has been removed. This protects against deleting a new device that reused + * the same name after the original was removed externally. + */ +struct purge_path_info { + struct list_head node; /* List linkage */ + struct udev_device *udev; /* Udev device (refcounted) */ + int fd; /* Dup'd fd prevents device reuse */ +}; + +/* + * Attempt to delete a path by writing to the SCSI device's sysfs delete + * attribute. This triggers kernel-level device removal. The actual cleanup + * of the path structure from pathvec happens later when a uevent arrives + * (handled by uev_remove_path). + * + * This function does NOT require vecs->lock to be held, as it operates on + * copied data. This function may block while writing to sysfs, which is + * why it's called without holding any locks. + * + * Protection against device reuse: + * The duplicated fd in purge_path_info prevents the kernel from reusing + * the device's minor number (and thus the device name like /dev/sdd) for + * a new device, even if the original device has been removed externally. + * This ensures we cannot accidentally delete a new device that reused the + * same name. The kernel maintains this guarantee as long as we hold the + * open file descriptor. + */ +static void delete_path_sysfs(struct purge_path_info *info) +{ + struct udev_device *ud; + const char *devname; + + if (!info->udev) + goto out; + + devname = udev_device_get_devnode(info->udev); + + /* + * Get the SCSI device parent. This is where we'll write to the + * "delete" attribute to trigger device removal. + */ + ud = udev_device_get_parent_with_subsystem_devtype(info->udev, "scsi", + "scsi_device"); + if (!ud) { + condlog(3, "%s: failed to purge, no SCSI parent found", devname); + goto out; + } + + /* + * Write "1" to the SCSI device's delete attribute to trigger + * kernel-level device removal. + */ + if (sysfs_attr_set_value(ud, "delete", "1", 1) < 0) + condlog(3, "%s: failed to purge", devname); + else + condlog(2, "%s: purged", devname); + +out: + return; +} + +/* + * Prepare purge info for a path while holding vecs->lock. + * Takes a reference on the udev device and duplicates the fd. + * Returns allocated purge_path_info on success, NULL on failure. + * + * We require a valid fd because it prevents the kernel from reusing + * the device's minor number (and device name) for a new device while + * we hold it open. This protects against accidentally deleting a new + * device that reused the same name after the original was removed. + */ +static struct purge_path_info *prepare_purge_path_info(struct path *pp) +{ + struct purge_path_info *info = NULL; + + if (!pp->udev || !pp->mpp) + goto out; + + /* + * We require a valid fd to prevent device name reuse. + * Without it, we cannot safely purge the device. + */ + if (pp->fd < 0) { + condlog(3, "%s: no fd available, cannot safely purge", pp->dev); + goto out; + } + + info = calloc(1, sizeof(*info)); + if (!info) + goto out; + + INIT_LIST_HEAD(&info->node); + info->udev = udev_device_ref(pp->udev); + if (!info->udev) + goto out_free; + + info->fd = dup(pp->fd); + if (info->fd < 0) { + condlog(3, "%s: failed to dup fd: %s, cannot safely purge", + pp->dev, strerror(errno)); + goto out_unref; + } + + return info; + +out_unref: + udev_device_unref(info->udev); +out_free: + free(info); + info = NULL; +out: + return info; +} + +/* + * Clean up and free purge info. + */ +static void free_purge_path_info(struct purge_path_info *info) +{ + if (!info) + return; + + if (info->fd >= 0) + close(info->fd); + if (info->udev) + udev_device_unref(info->udev); + free(info); +} + +/* + * Build a list of purge_path_info for all paths marked for purge. + * This should be called while holding vecs->lock. It clears the + * disconnected flag and prepares purge info for each path, adding + * them to tmpq. + */ +void build_purge_list(struct vectors *vecs, struct list_head *tmpq) +{ + struct path *pp; + unsigned int i; + + vector_foreach_slot (vecs->pathvec, pp, i) { + struct purge_path_info *info; + + if (pp->disconnected != DISCONNECTED_READY_FOR_PURGE) + continue; + + /* + * Mark as queued whether we succeed or fail. + * On success, we're purging it now. + * On failure, retrying is unlikely to help until + * the checker re-evaluates the path. + */ + pp->disconnected = DISCONNECTED_QUEUED_FOR_PURGE; + + info = prepare_purge_path_info(pp); + if (info) { + condlog(2, "%s: queuing path for purge", pp->dev); + list_add_tail(&info->node, tmpq); + } else + condlog(3, "%s: failed to prepare purge info", pp->dev); + } +} + +static void rcu_unregister(__attribute__((unused)) void *param) +{ + rcu_unregister_thread(); +} + +/* + * Cleanup handler for a single purge_path_info. + * Used to prevent memory leaks if thread is cancelled while processing. + */ +static void cleanup_purge_path_info(void *arg) +{ + struct purge_path_info *info = arg; + + free_purge_path_info(info); +} + +/* + * Cleanup handler for purge list. Frees all purge_path_info entries. + * Can be called as a pthread cleanup handler or directly. + */ +void cleanup_purge_list(void *arg) +{ + struct list_head *purge_list = arg; + struct purge_path_info *info, *tmp; + + list_for_each_entry_safe(info, tmp, purge_list, node) + { + list_del_init(&info->node); + free_purge_path_info(info); + } +} + +/* + * Cleanup handler for the global purge queue. + * Used during shutdown to free any remaining queued items. + */ +static void cleanup_global_purge_queue(void *arg __attribute__((unused))) +{ + pthread_mutex_lock(&purge_mutex); + cleanup_purge_list(&purge_queue); + pthread_mutex_unlock(&purge_mutex); +} + +/* + * Main purge thread loop. + * + * This thread waits for purge_path_info structs to be queued by the checker + * thread, then processes them by writing to their sysfs delete attributes. + * The checker thread builds the list while holding vecs->lock, so this + * thread doesn't need to grab that lock at all. + * + * Uses list_splice_tail_init() like uevent_dispatch() to safely transfer + * items from the global queue to a local list for processing. + * + * Cleanup handlers are registered for both the local purge_list and the + * global purge_queue (similar to uevent_listen), and for each individual + * purge_path_info after it's popped off the list (similar to service_uevq). + * This ensures no memory leaks if the thread is cancelled at any point. + */ +void *purgeloop(void *ap __attribute__((unused))) +{ + pthread_cleanup_push(rcu_unregister, NULL); + rcu_register_thread(); + mlockall(MCL_CURRENT | MCL_FUTURE); + + /* + * Cleanup handler for global purge_queue. + * This handles items that were queued but not yet moved to purge_list. + */ + pthread_cleanup_push(cleanup_global_purge_queue, NULL); + + while (1) { + LIST_HEAD(purge_list); + struct purge_path_info *info; + + /* + * Cleanup handler for local purge_list. + * This handles items that were moved from purge_queue but + * not yet processed. + */ + pthread_cleanup_push(cleanup_purge_list, &purge_list); + + /* + * Cleanup handler for purge_mutex. + * Note: pthread_cond_wait() reacquires the mutex before + * returning, even on cancellation, so this cleanup handler + * will properly unlock it if we're cancelled. + */ + pthread_cleanup_push(cleanup_mutex, &purge_mutex); + pthread_mutex_lock(&purge_mutex); + pthread_testcancel(); + while (list_empty(&purge_queue)) { + condlog(4, "purgeloop waiting for work"); + pthread_cond_wait(&purge_cond, &purge_mutex); + } + list_splice_tail_init(&purge_queue, &purge_list); + pthread_cleanup_pop(1); + + /* + * Process all paths in the list without holding any locks. + * The sysfs operations may block, but that's fine since we're + * not holding vecs->lock. + * + * After popping each info off the list, we immediately push + * a cleanup handler for it. This ensures it gets freed even + * if we're cancelled inside delete_path_sysfs(). + */ + while ((info = list_pop_entry(&purge_list, typeof(*info), node))) { + pthread_cleanup_push(cleanup_purge_path_info, info); + delete_path_sysfs(info); + pthread_cleanup_pop(1); + } + + /* + * Pop cleanup handler without executing it (0) since we've + * already freed everything above. The handler only runs if + * the thread is cancelled during processing. + */ + pthread_cleanup_pop(0); + } + + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + return NULL; +} diff --git a/multipathd/purge.h b/multipathd/purge.h new file mode 100644 index 000000000..1fe755f3e --- /dev/null +++ b/multipathd/purge.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Brian Bunker + * Copyright (C) 2025 Krishna Kant + */ + +#ifndef PURGE_H_INCLUDED +#define PURGE_H_INCLUDED + +#include +#include "list.h" + +struct vectors; + +/* + * Purge thread synchronization. + * The checker thread builds a list of paths to purge and queues them here. + * The purge thread picks up the queue and processes it. + */ +extern pthread_mutex_t purge_mutex; +extern pthread_cond_t purge_cond; +extern struct list_head purge_queue; + +/* + * Build a list of paths to purge and add them to tmpq. Called by checker + * thread while holding vecs->lock. + */ +void build_purge_list(struct vectors *vecs, struct list_head *tmpq); + +/* + * Cleanup handler for purge list. Frees all purge_path_info entries. + * Can be called as a pthread cleanup handler or directly for shutdown cleanup. + */ +void cleanup_purge_list(void *arg); + +/* + * Main purge thread loop + */ +void *purgeloop(void *ap); + +#endif /* PURGE_H_INCLUDED */ diff --git a/tests/Makefile b/tests/Makefile index 28c00ad2d..9f1b950f8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,7 +5,7 @@ TESTDIR := $(CURDIR) CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -I$(daemondir) \ -DTESTCONFDIR=\"$(TESTDIR)/conf.d\" -CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS) +CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter -Wno-unused-function $(W_MISSING_INITIALIZERS) LIBDEPS += -L. -L $(mpathutildir) -L$(mpathcmddir) -lmultipath -lmpathutil -lmpathcmd -lcmocka TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \ @@ -39,26 +39,30 @@ dmevents-test_OBJDEPS = $(multipathdir)/devmapper.o dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu hwtable-test_TESTDEPS := test-lib.o hwtable-test_OBJDEPS := $(multipathdir)/discovery.o $(multipathdir)/blacklist.o \ - $(multipathdir)/structs_vec.o $(multipathdir)/structs.o $(multipathdir)/propsel.o + $(multipathdir)/structs_vec.o $(multipathdir)/structs.o $(multipathdir)/propsel.o \ + $(mpathutildir)/mt-libudev.o hwtable-test_LIBDEPS := -ludev -lpthread -ldl blacklist-test_TESTDEPS := test-log.o -blacklist-test_LIBDEPS := -ludev -vpd-test_OBJDEPS := $(multipathdir)/discovery.o +blacklist-test_OBJDEPS := $(mpathutildir)/mt-libudev.o +blacklist-test_LIBDEPS := -ludev -lpthread -ldl +vpd-test_OBJDEPS := $(multipathdir)/discovery.o $(mpathutildir)/mt-libudev.o vpd-test_LIBDEPS := -ludev -lpthread -ldl alias-test_TESTDEPS := test-log.o -alias-test_OBJDEPS := $(mpathutildir)/util.o +alias-test_OBJDEPS := $(mpathutildir)/util.o $(mpathutildir)/mt-libudev.o alias-test_LIBDEPS := -ludev -lpthread -ldl -valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o +valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o $(mpathutildir)/mt-libudev.o valid-test_LIBDEPS := -lmount -ludev -lpthread -ldl -devt-test_LIBDEPS := -ludev +devt-test_LIBDEPS := -ludev -lpthread -ldl +devt-test_OBJDEPS := $(mpathutildir)/mt-libudev.o mpathvalid-test_LIBDEPS := -ludev -lpthread -ldl -mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o +mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o $(mpathutildir)/mt-libudev.o directio-test_LIBDEPS := -laio strbuf-test_OBJDEPS := $(mpathutildir)/strbuf.o sysfs-test_TESTDEPS := test-log.o -sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o +sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o $(mpathutildir)/mt-libudev.o sysfs-test_LIBDEPS := -ludev -lpthread -ldl features-test_LIBDEPS := -ludev -lpthread +features-test_OBJDEPS := $(mpathutildir)/mt-libudev.o cli-test_OBJDEPS := $(daemondir)/cli.o mapinfo-test_LIBDEPS = -lpthread -ldevmapper diff --git a/tests/alias.c b/tests/alias.c index 163d9a7d5..faafcd27f 100644 --- a/tests/alias.c +++ b/tests/alias.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "strbuf.h" #include "util.h" #include "alias.h" @@ -59,9 +59,9 @@ ssize_t __wrap_write(int fd, const void *buf, size_t count) start = (const char *)buf + sizeof(BINDINGS_FILE_HEADER) - 1; else start = buf; - binding = mock_ptr_type(char *); + binding = mock_ptr_type(const char *); start = strstr(start, binding); - check_expected(count); + check_expected_uint(count); assert_ptr_not_equal(start, NULL); return set_errno__(mock_type(int)); } @@ -80,12 +80,12 @@ int __wrap_dm_get_wwid(const char *name, char *uuid, int uuid_len) { int ret; - check_expected(name); - check_expected(uuid_len); + check_expected_ptr(name); + check_expected_int(uuid_len); assert_non_null(uuid); ret = mock_type(int); if (ret == DMP_OK) - strcpy(uuid, mock_ptr_type(char *)); + strcpy(uuid, mock_ptr_type(const char *)); return ret; } @@ -389,7 +389,7 @@ static void sd_fd_many(void **state) for (i = 1; i < 5000; i++) { rc = format_devname__(buf, i, sizeof(buf), "MPATH"); - assert_in_range(rc, 6, 8); + assert_int_in_range(rc, 6, 8); rc = scan_devname(buf, "MPATH"); assert_int_equal(rc, i); } @@ -404,7 +404,7 @@ static void sd_fd_random(void **state) for (i = 1; i < 1000; i++) { n = random() & 0xffff; rc = format_devname__(buf, n, sizeof(buf), "MPATH"); - assert_in_range(rc, 6, 9); + assert_int_in_range(rc, 6, 9); rc = scan_devname(buf, "MPATH"); assert_int_equal(rc, n); } @@ -437,14 +437,14 @@ static int test_scan_devname(void) static void mock_unused_alias(const char *alias) { expect_string(__wrap_dm_get_wwid, name, alias); - expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); + expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); will_return(__wrap_dm_get_wwid, DMP_NOT_FOUND); } static void mock_self_alias(const char *alias, const char *wwid) { expect_string(__wrap_dm_get_wwid, name, alias); - expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); + expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); will_return(__wrap_dm_get_wwid, DMP_OK); will_return(__wrap_dm_get_wwid, wwid); } @@ -470,14 +470,14 @@ static void mock_self_alias(const char *alias, const char *wwid) #define mock_failed_alias(alias, wwid) \ do { \ expect_string(__wrap_dm_get_wwid, name, alias); \ - expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); \ + expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); \ will_return(__wrap_dm_get_wwid, DMP_NOT_FOUND); \ } while (0) #define mock_used_alias(alias, wwid) \ do { \ expect_string(__wrap_dm_get_wwid, name, alias); \ - expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); \ + expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE); \ will_return(__wrap_dm_get_wwid, DMP_OK); \ will_return(__wrap_dm_get_wwid, "WWID_USED"); \ expect_condlog(3, USED_STR(alias, wwid)); \ @@ -504,7 +504,7 @@ static void mock_bindings_file__(const char *content, bool conflict_ok) continue; rc = add_binding(&global_bindings, alias, wwid); - assert_in_set(rc, values, conflict_ok ? 2 : 1); + assert_int_in_set(rc, values, (conflict_ok ? 2 : 1)); } } @@ -1260,7 +1260,8 @@ static void al_a(void **state) static const char ln[] = "MPATHa WWIDa\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_rename, 0); @@ -1279,7 +1280,8 @@ static void al_zz(void **state) static const char ln[] = "MPATHzz WWIDzz\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_rename, 0); @@ -1318,10 +1320,11 @@ static void al_write_partial(void **state) static const char ln[] = "MPATHa WWIDa\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1); - expect_value(__wrap_write, count, 1); + expect_uint_value(__wrap_write, count, 1); will_return(__wrap_write, ln + sizeof(ln) - 2); will_return(__wrap_write, 1); will_return(__wrap_rename, 0); @@ -1340,10 +1343,11 @@ static void al_write_short(void **state) static const char ln[] = "MPATHa WWIDa\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1); - expect_value(__wrap_write, count, 1); + expect_uint_value(__wrap_write, count, 1); will_return(__wrap_write, ln + sizeof(ln) - 2); will_return(__wrap_write, 0); expect_condlog(2, "write_bindings_file: short write"); @@ -1360,7 +1364,8 @@ static void al_write_err(void **state) static const char ln[] = "MPATHa WWIDa\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, -EPERM); expect_condlog(1, "failed to write new bindings file"); @@ -1376,7 +1381,8 @@ static void al_rename_err(void **state) static const char ln[] = "MPATHa WWIDa\n"; char *alias; - expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); + expect_uint_value(__wrap_write, count, + strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_write, ln); will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln)); will_return(__wrap_rename, -EROFS); @@ -1408,7 +1414,7 @@ static int test_allocate_binding(void) do { \ static const char ln[] = BINDING_STR(alias, wwid); \ \ - expect_value(__wrap_write, count, \ + expect_uint_value(__wrap_write, count, \ strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \ will_return(__wrap_write, ln); \ will_return(__wrap_write, \ diff --git a/tests/blacklist.c b/tests/blacklist.c index ab3da619b..5bf2fd149 100644 --- a/tests/blacklist.c +++ b/tests/blacklist.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "globals.c" #include "blacklist.h" #include "test-log.h" diff --git a/tests/cli.c b/tests/cli.c index 9e2ad3c52..fbcca0958 100644 --- a/tests/cli.c +++ b/tests/cli.c @@ -1,14 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Martin Wilck, SUSE - * - * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include -#include +#include "cmocka-compat.h" #include @@ -49,7 +48,7 @@ static void client_test_##NAME(void **state) \ assert_int_equal(get_cmdvec(cmd, &v, false), R); \ if (R == 0) { \ assert_ptr_not_equal(v, NULL); \ - assert_int_equal(fingerprint(v), FPR); \ + assert_uint_equal(fingerprint(v), FPR); \ if (GOOD) \ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \ else \ @@ -73,7 +72,7 @@ static void client_param_##NAME(void **state) \ \ assert_int_equal(get_cmdvec(cmd, &v, false), 0); \ assert_ptr_not_equal(v, NULL); \ - assert_int_equal(fingerprint(v), FPR); \ + assert_uint_equal(fingerprint(v), FPR); \ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \ assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR); \ free_keys(v); \ @@ -95,7 +94,7 @@ static void client_2param_##NAME(void **state) \ \ assert_int_equal(get_cmdvec(cmd, &v, false), 0); \ assert_ptr_not_equal(v, NULL); \ - assert_int_equal(fingerprint(v), FPR); \ + assert_uint_equal(fingerprint(v), FPR); \ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \ assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR1); \ assert_string_equal(((struct key *)VECTOR_SLOT(v, N))->param, PARN); \ diff --git a/tests/cmocka-compat.h b/tests/cmocka-compat.h new file mode 100644 index 000000000..8ec6ed243 --- /dev/null +++ b/tests/cmocka-compat.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2015 Martin Wilck, SUSE LLC + */ +#include + +#if CMOCKA_VERSION < 0x020000 +#define assert_int_in_range(x, low, high) assert_in_range(x, low, high) +#define assert_uint_in_range(x, low, high) assert_in_range(x, low, high) +#define assert_uint_equal(x, y) assert_int_equal(x, y) +#define check_expected_int(x) check_expected(x) +#define check_expected_uint(x) check_expected(x) +#define expect_int_value(f, x, y) expect_value(f, x, y) +#define expect_uint_value(f, x, y) expect_value(f, x, y) +#define assert_int_in_set(x, vals, count) assert_in_set(x, vals, count) +#endif diff --git a/tests/devt.c b/tests/devt.c index aafee383c..711947cb5 100644 --- a/tests/devt.c +++ b/tests/devt.c @@ -1,15 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Martin Wilck, SUSE - * - * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include -#include -#include +#include "cmocka-compat.h" +#include "mt-udev-wrap.h" #include #include #include @@ -173,7 +172,7 @@ static void test_devt2devname_real(void **state) enm = udev_enumerate_new(udev); assert_non_null(enm); r = udev_enumerate_add_match_subsystem(enm, "block"); - assert_in_range(r, 0, INT_MAX); + assert_int_in_range(r, 0, INT_MAX); r = udev_enumerate_scan_devices(enm); first = udev_enumerate_get_list_entry(enm); udev_list_entry_foreach(item, first) { diff --git a/tests/directio.c b/tests/directio.c index 534443447..fa1e553a5 100644 --- a/tests/directio.c +++ b/tests/directio.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "wrap64.h" #include "globals.c" #include "../libmultipath/checkers/directio.c" @@ -38,7 +38,7 @@ int WRAP_IOCTL(int fd, ioctl_request_t request, void *argp) int *blocksize = (int *)argp; if (test_dev) { - mock_type(int); + (void)mock_type(int); return REAL_IOCTL(fd, request, argp); } @@ -51,7 +51,12 @@ int WRAP_IOCTL(int fd, ioctl_request_t request, void *argp) * BLKSZGET must be cast to "int" and back to "unsigned long", * otherwise the assertion below will fail. */ +#ifdef __GLIBC__ + assert_uint_equal(request, (ioctl_request_t)BLKBSZGET); +#else assert_int_equal(request, (ioctl_request_t)BLKBSZGET); +#endif + assert_non_null(blocksize); *blocksize = mock_type(int); return 0; @@ -138,9 +143,9 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout) { int nr_evs; - struct timespec *sleep_tmo; + const struct timespec *sleep_tmo; int i; - struct io_event *evs; + const struct io_event *evs; assert_non_null(timeout); nr_evs = mock_type(int); @@ -149,8 +154,8 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr, return 0; if (test_dev) { int n = 0; - mock_ptr_type(struct timespec *); - mock_ptr_type(struct io_event *); + (void)mock_ptr_type(const struct timespec *); + (void)mock_ptr_type(const struct io_event *); condlog(2, "min_nr = %ld nr_evs = %d", min_nr, nr_evs); while (n < nr_evs) { @@ -160,7 +165,7 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr, } assert_int_equal(nr_evs, n); } else { - sleep_tmo = mock_ptr_type(struct timespec *); + sleep_tmo = mock_ptr_type(const struct timespec *); if (sleep_tmo) { if (sleep_tmo->tv_sec < 0) nanosleep(timeout, NULL); @@ -171,7 +176,7 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr, errno = -nr_evs; return -1; } - evs = mock_ptr_type(struct io_event *); + evs = mock_ptr_type(const struct io_event *); for (i = 0; i < nr_evs; i++) events[i] = evs[i]; } @@ -259,7 +264,7 @@ static void do_libcheck_init(struct checker *c, int blocksize, int timeout, *req = ct->req; if (!test_dev) /* don't check fake blocksize on real devices */ - assert_int_equal(ct->req->blksize, blocksize); + assert_uint_equal(ct->req->blksize, blocksize); } static int is_checker_running(struct checker *c) diff --git a/tests/dmevents.c b/tests/dmevents.c index 6df4f0362..c94ab016f 100644 --- a/tests/dmevents.c +++ b/tests/dmevents.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include #include @@ -244,7 +244,7 @@ int WRAP_IOCTL(int fd, unsigned long request, void *argp) { condlog(1, "%s %ld", __func__, request); assert_int_equal(fd, waiter->fd); - assert_int_equal(request, DM_DEV_ARM_POLL); + assert_uint_equal(request, DM_DEV_ARM_POLL); return mock_type(int); } @@ -295,7 +295,7 @@ void __wrap_dm_task_destroy(struct dm_task *dmt) int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - assert_int_equal(nfds, 1); + assert_uint_equal(nfds, 1); assert_int_equal(timeout, -1); assert_int_equal(fds->fd, waiter->fd); assert_int_equal(fds->events, POLLIN); @@ -304,7 +304,7 @@ int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs) { - check_expected(alias); + check_expected_ptr(alias); assert_ptr_equal(vecs, waiter->vecs); } @@ -315,7 +315,7 @@ int __wrap_update_multipath(struct vectors *vecs, char *mapname) { int fail; - check_expected(mapname); + check_expected_ptr(mapname); assert_ptr_equal(vecs, waiter->vecs); fail = mock_type(int); if (fail) { @@ -435,7 +435,7 @@ static void test_watch_dmevents_good0(void **state) /* verify foo is being watched */ dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 1); unwatch_dmevents("foo"); @@ -459,14 +459,14 @@ static void test_watch_dmevents_good1(void **state) assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(add_dm_device_event("foo", 1, 6), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 6); + assert_uint_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 1); unwatch_dmevents("foo"); @@ -490,18 +490,18 @@ static void test_watch_dmevents_good2(void **state) assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_ptr_equal(find_dmevents("bar"), NULL); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("bar"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 7); + assert_uint_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 2); unwatch_all_dmevents(); @@ -597,15 +597,15 @@ static void test_get_events_good1(void **state) assert_int_equal(dm_get_events(), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 6); + assert_uint_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_UPDATE); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 7); + assert_uint_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev_evt = find_dmevents("xyzzy"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 8); + assert_uint_equal(dev_evt->evt_nr, 8); assert_int_equal(dev_evt->action, EVENT_REMOVE); assert_ptr_equal(find_dmevents("baz"), NULL); assert_ptr_equal(find_dmevents("qux"), NULL); @@ -632,12 +632,12 @@ static void test_dmevent_loop_bad0(void **state) assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 6); - assert_int_equal(dev->update_nr, 5); + assert_uint_equal(dev->evt_nr, 6); + assert_uint_equal(dev->update_nr, 5); } /* arm_dm_event_poll's ioctl fails. Nothing happens */ @@ -654,12 +654,12 @@ static void test_dmevent_loop_bad1(void **state) assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 6); - assert_int_equal(dev->update_nr, 5); + assert_uint_equal(dev->evt_nr, 6); + assert_uint_equal(dev->update_nr, 5); } /* dm_get_events fails. Nothing happens */ @@ -677,12 +677,12 @@ static void test_dmevent_loop_bad2(void **state) assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 5); + assert_uint_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 6); - assert_int_equal(dev->update_nr, 5); + assert_uint_equal(dev->evt_nr, 6); + assert_uint_equal(dev->update_nr, 5); } /* verify dmevent_loop runs successfully when no devices are being @@ -743,20 +743,20 @@ static void test_dmevent_loop_good1(void **state) assert_int_equal(VECTOR_SIZE(data.dm_devices), 3); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 6); + assert_uint_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 6); - assert_int_equal(dev->update_nr, 6); + assert_uint_equal(dev->evt_nr, 6); + assert_uint_equal(dev->update_nr, 6); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 7); + assert_uint_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 7); - assert_int_equal(dev->update_nr, 7); + assert_uint_equal(dev->evt_nr, 7); + assert_uint_equal(dev->update_nr, 7); assert_ptr_equal(find_dmevents("xyzzy"), NULL); assert_ptr_equal(find_dm_device("xyzzy"), NULL); } @@ -791,20 +791,20 @@ static void test_dmevent_loop_good2(void **state) assert_int_equal(VECTOR_SIZE(data.dm_devices), 2); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 6); + assert_uint_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 6); - assert_int_equal(dev->update_nr, 6); + assert_uint_equal(dev->evt_nr, 6); + assert_uint_equal(dev->update_nr, 6); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); - assert_int_equal(dev_evt->evt_nr, 9); + assert_uint_equal(dev_evt->evt_nr, 9); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 9); - assert_int_equal(dev->update_nr, 9); + assert_uint_equal(dev->evt_nr, 9); + assert_uint_equal(dev->update_nr, 9); assert_ptr_equal(find_dmevents("baz"), NULL); assert_ptr_equal(find_dm_device("baz"), NULL); } @@ -831,8 +831,8 @@ static void test_dmevent_loop_good3(void **state) assert_int_equal(VECTOR_SIZE(data.dm_devices), 1); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); - assert_int_equal(dev->evt_nr, 9); - assert_int_equal(dev->update_nr, 9); + assert_uint_equal(dev->evt_nr, 9); + assert_uint_equal(dev->update_nr, 9); assert_ptr_equal(find_dmevents("foo"), NULL); assert_ptr_equal(find_dmevents("bar"), NULL); assert_ptr_equal(find_dm_device("foo"), NULL); diff --git a/tests/features.c b/tests/features.c index 31f978fd3..35f15de61 100644 --- a/tests/features.c +++ b/tests/features.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "../libmultipath/propsel.c" #include "globals.c" diff --git a/tests/hwtable.c b/tests/hwtable.c index 334b75e8c..6021bfd36 100644 --- a/tests/hwtable.c +++ b/tests/hwtable.c @@ -7,8 +7,8 @@ #include #include #include -#include -#include +#include "cmocka-compat.h" +#include "mt-udev-wrap.h" #include #include #include @@ -505,7 +505,7 @@ static void replicate_config(const struct hwt_state *hwt, bool local) DUMP_CFG_STR(cfg2); #endif - assert_int_equal(strlen(cfg2), strlen(cfg1)); + assert_uint_equal(strlen(cfg2), strlen(cfg1)); assert_string_equal(cfg2, cfg1); free(cfg1); free(cfg2); diff --git a/tests/mapinfo.c b/tests/mapinfo.c index 36511607d..12540f9fa 100644 --- a/tests/mapinfo.c +++ b/tests/mapinfo.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2024 Martin Wilck, SUSE - * SPDX-License-Identifier: GPL-2.0-or-later */ /* @@ -25,7 +25,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "util.h" #include "devmapper.h" #include "globals.c" @@ -87,10 +87,10 @@ void __wrap_dm_task_destroy(struct dm_task *t) { } -struct dm_task *__wrap_dm_task_create(int task) +const struct dm_task *__wrap_dm_task_create(int task) { - check_expected(task); - return mock_ptr_type(void *); + check_expected_int(task); + return mock_ptr_type(const struct dm_task *); } int __wrap_dm_task_run(struct dm_task *t) @@ -114,25 +114,25 @@ int __wrap_dm_task_get_errno(struct dm_task *t) int __wrap_dm_task_set_name(struct dm_task *t, const char *name) { - check_expected(name); + check_expected_ptr(name); return mock_type(int); } int __wrap_dm_task_set_uuid(struct dm_task *t, const char *uuid) { - check_expected(uuid); + check_expected_ptr(uuid); return mock_type(int); } int __wrap_dm_task_set_major(struct dm_task *t, int val) { - check_expected(val); + check_expected_int(val); return mock_type(int); } int __wrap_dm_task_set_minor(struct dm_task *t, int val) { - check_expected(val); + check_expected_int(val); return mock_type(int); } @@ -151,22 +151,23 @@ int __wrap_dm_task_get_info(struct dm_task *t, struct dm_info *dmi) assert_non_null(dmi); if (rc) { - struct dm_info *info = mock_ptr_type(struct dm_info *); + const struct dm_info *info = mock_ptr_type(const struct dm_info *); memcpy(dmi, info, sizeof(*dmi)); } return rc; } -void * __wrap_dm_get_next_target(struct dm_task *dmt, void *next, - uint64_t *start, uint64_t *length, - char **target_type, char **params) +const void * +__wrap_dm_get_next_target(struct dm_task *dmt, void *next, uint64_t *start, + uint64_t *length, const char **target_type, + const char **params) { *start = 0; *length = mock_type(uint64_t); - *target_type = mock_ptr_type(char *); - *params = mock_ptr_type(char *); - return mock_ptr_type(void *); + *target_type = mock_ptr_type(const char *); + *params = mock_ptr_type(const char *); + return mock_ptr_type(const void *); } static void mock_dm_get_next_target(uint64_t len, const char *target_type, @@ -191,11 +192,11 @@ const char *__wrap_dm_task_get_uuid(struct dm_task *t) static void mock_mapinfo_name_1(int ioctl_nr, int create_rc, const char *name, int name_rc, int run_rc, int err) { - expect_value(__wrap_dm_task_create, task, ioctl_nr); + expect_int_value(__wrap_dm_task_create, task, ioctl_nr); will_return(__wrap_dm_task_create, create_rc); if (create_rc == 0) return; - expect_value(__wrap_dm_task_set_name, name, name); + expect_string(__wrap_dm_task_set_name, name, name); will_return(__wrap_dm_task_set_name, name_rc); if (name_rc == 0) return; @@ -223,7 +224,7 @@ static void test_mapinfo_bad_mapid(void **state) int rc; /* can't use mock_mapinfo_name() here because of invalid id type */ - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); rc = libmp_mapinfo(DM_MAP_BY_NAME + 100, (mapid_t) { .str = "foo", }, @@ -646,9 +647,9 @@ static void test_mapinfo_bad_set_uuid(void **state) { int rc; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_uuid, uuid, "foo"); + expect_string(__wrap_dm_task_set_uuid, uuid, "foo"); will_return(__wrap_dm_task_set_uuid, 0); rc = libmp_mapinfo(DM_MAP_BY_UUID, (mapid_t) { .str = "foo", }, @@ -660,9 +661,9 @@ static void test_mapinfo_bad_set_dev_01(void **state) { int rc; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 0); rc = libmp_mapinfo(DM_MAP_BY_DEV, (mapid_t) { ._u = { 254, 123 } }, @@ -674,11 +675,11 @@ static void test_mapinfo_bad_set_dev_02(void **state) { int rc; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 1); - expect_value(__wrap_dm_task_set_minor, val, 123); + expect_int_value(__wrap_dm_task_set_minor, val, 123); will_return(__wrap_dm_task_set_minor, 0); rc = libmp_mapinfo(DM_MAP_BY_DEV, (mapid_t) { ._u = { 254, 123 } }, @@ -691,9 +692,9 @@ static void test_mapinfo_bad_set_dev_03(void **state) int rc; dev_t devt = makedev(254, 123); - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 0); rc = libmp_mapinfo(DM_MAP_BY_DEVT, (mapid_t) { .devt = devt }, @@ -706,11 +707,11 @@ static void test_mapinfo_bad_set_dev_04(void **state) int rc; dev_t devt = makedev(254, 123); - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 1); - expect_value(__wrap_dm_task_set_minor, val, 123); + expect_int_value(__wrap_dm_task_set_minor, val, 123); will_return(__wrap_dm_task_set_minor, 0); rc = libmp_mapinfo(DM_MAP_BY_DEVT, (mapid_t) { .devt = devt }, @@ -738,9 +739,9 @@ static void test_mapinfo_good_by_uuid_info(void **state) int rc; struct dm_info dmi; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_uuid, uuid, "foo"); + expect_string(__wrap_dm_task_set_uuid, uuid, "foo"); will_return(__wrap_dm_task_set_uuid, 1); will_return(__wrap_dm_task_run, 1); WRAP_DM_TASK_GET_INFO(1); @@ -757,11 +758,11 @@ static void test_mapinfo_good_by_dev_info(void **state) int rc; struct dm_info dmi; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 1); - expect_value(__wrap_dm_task_set_minor, val, 123); + expect_int_value(__wrap_dm_task_set_minor, val, 123); will_return(__wrap_dm_task_set_minor, 1); will_return(__wrap_dm_task_run, 1); WRAP_DM_TASK_GET_INFO(1); @@ -779,11 +780,11 @@ static void test_mapinfo_good_by_devt_info(void **state) int rc; struct dm_info dmi; - expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); + expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO); will_return(__wrap_dm_task_create, 1); - expect_value(__wrap_dm_task_set_major, val, 254); + expect_int_value(__wrap_dm_task_set_major, val, 254); will_return(__wrap_dm_task_set_major, 1); - expect_value(__wrap_dm_task_set_minor, val, 123); + expect_int_value(__wrap_dm_task_set_minor, val, 123); will_return(__wrap_dm_task_set_minor, 1); will_return(__wrap_dm_task_run, 1); WRAP_DM_TASK_GET_INFO(1); @@ -871,7 +872,7 @@ static void test_mapinfo_good_size(void **state) (mapid_t) { .str = "foo", }, (mapinfo_t) { .size = &size }); assert_int_equal(rc, DMP_OK); - assert_int_equal(size, 12345); + assert_uint_equal(size, 12345); } static void test_mapinfo_bad_next_target_01(void **state) @@ -909,7 +910,7 @@ static void test_mapinfo_bad_next_target_02(void **state) (mapid_t) { .str = "foo", }, (mapinfo_t) { .dmi = &dmi, .name = name, .uuid = uuid, .size = &size }); assert_int_equal(rc, DMP_EMPTY); - assert_int_equal(size, 0); + assert_uint_equal(size, 0); assert_memory_equal(&dmi, &MPATH_DMI_02, sizeof(dmi)); assert_true(!strcmp(name, MPATH_NAME_01)); assert_true(!strcmp(uuid, MPATH_UUID_01)); @@ -1102,7 +1103,7 @@ static void test_mapinfo_no_table_02(void **state) (mapid_t) { .str = "foo", }, (mapinfo_t) { .dmi = &dmi, .name = name, .uuid = uuid, .size = &size }); assert_int_equal(rc, DMP_EMPTY); - assert_int_equal(size, 0); + assert_uint_equal(size, 0); assert_memory_equal(&dmi, &MPATH_DMI_02, sizeof(dmi)); assert_true(!strcmp(name, MPATH_NAME_01)); assert_true(!strcmp(uuid, MPATH_UUID_01)); @@ -1369,7 +1370,7 @@ static void test_mapinfo_good_all_01(void **state) assert_int_equal(rc, DMP_OK); assert_non_null(status); assert_non_null(target); - assert_int_equal(size, 12345); + assert_uint_equal(size, 12345); assert_memory_equal(&dmi, &MPATH_DMI_01, sizeof(dmi)); assert_true(!strcmp(target, MPATH_TARGET_01)); assert_true(!strcmp(status, MPATH_STATUS_01)); diff --git a/tests/mpathvalid.c b/tests/mpathvalid.c index 134dfc9d6..0a70c6148 100644 --- a/tests/mpathvalid.c +++ b/tests/mpathvalid.c @@ -1,7 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Benjamin Marzinski, Red Hat - * - * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include @@ -9,8 +8,8 @@ #include #include #include -#include -#include +#include "mt-udev-wrap.h" +#include "cmocka-compat.h" #include "structs.h" #include "config.h" #include "mpath_valid.h" @@ -66,11 +65,11 @@ int __wrap_is_path_valid(const char *name, struct config *conf, struct path *pp, assert_ptr_not_equal(pp, NULL); assert_true(check_multipathd); - assert_int_equal(findmp, conf->find_multipaths); + assert_int_equal(findmp, conf->find_multipaths); if (r == MPATH_IS_ERROR || r == MPATH_IS_NOT_VALID) return r; - - strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE); + + strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE); return r; } @@ -223,7 +222,7 @@ static void check_mpathvalid_init(int findmp, int prio, int log_style) check_config(true); assert_int_equal(logsink, log_style); assert_int_equal(libmp_verbosity, prio); - assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode()); + assert_uint_equal(findmp_to_mode(findmp), mpathvalid_get_mode()); } static void check_mpathvalid_exit(void) @@ -262,9 +261,9 @@ static void test_mpathvalid_exit(void **state) static void test_mpathvalid_get_mode_bad(void **state) { #if 1 - assert_int_equal(mpathvalid_get_mode(), MPATH_MODE_ERROR); + assert_uint_equal(mpathvalid_get_mode(), MPATH_MODE_ERROR); #else - assert_int_equal(mpathvalid_get_mode(), 1); + assert_uint_equal(mpathvalid_get_mode(), 1); #endif } @@ -298,7 +297,7 @@ static void check_mpathvalid_reload_config(int findmp) will_return(__wrap_init_config, findmp); assert_int_equal(mpathvalid_reload_config(), 0); check_config(true); - assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode()); + assert_uint_equal(findmp_to_mode(findmp), mpathvalid_get_mode()); } static void test_mpathvalid_reload_config_good(void **state) diff --git a/tests/parser.c b/tests/parser.c index efa7bf2b6..0e718c9b8 100644 --- a/tests/parser.c +++ b/tests/parser.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include "cmocka-compat.h" // #include "list.h" #include "parser.h" #include "vector.h" diff --git a/tests/pgpolicy.c b/tests/pgpolicy.c index fb956d140..a37aa8e4a 100644 --- a/tests/pgpolicy.c +++ b/tests/pgpolicy.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include "globals.c" @@ -131,7 +131,9 @@ static int setup_null(void **state) static int teardownX(struct multipath *mp) { - free_pgvec(mp->pg, KEEP_PATHS); + vector_free(mp->paths); + mp->paths = NULL; + free_pgvec(mp->pg); mp->pg = NULL; return 0; } @@ -165,10 +167,9 @@ static void verify_pathgroups(struct multipath *mp, struct path *pp, int **groups, int *group_size, int *marginal, int size) { - int i, j; + int i, j, sum = 0; struct pathgroup *pgp; - assert_null(mp->paths); assert_non_null(mp->pg); assert_int_equal(VECTOR_SIZE(mp->pg), size); for (i = 0; i < size; i++) { @@ -176,6 +177,7 @@ verify_pathgroups(struct multipath *mp, struct path *pp, int **groups, assert_non_null(pgp); assert_non_null(pgp->paths); assert_int_equal(VECTOR_SIZE(pgp->paths), group_size[i]); + sum += group_size[i]; if (marginal) assert_int_equal(pgp->marginal, marginal[i]); else @@ -192,6 +194,7 @@ verify_pathgroups(struct multipath *mp, struct path *pp, int **groups, assert_ptr_equal(pgp_path, pp_path); } } + assert_int_equal(sum, VECTOR_SIZE(mp->paths)); } static void test_one_group8(void **state) diff --git a/tests/strbuf.c b/tests/strbuf.c index 1cc161898..8d0beb804 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-only */ #define _GNU_SOURCE @@ -9,7 +9,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include "strbuf.h" #include "debug.h" @@ -24,7 +24,11 @@ void *__wrap_realloc(void *ptr, size_t size) if (!mock_realloc) return __real_realloc(ptr, size); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" p = mock_ptr_type(void *); +#pragma GCC diagnostic pop + condlog(4, "%s: %p, %zu -> %p", __func__, ptr, size, p); return p; } @@ -35,44 +39,44 @@ static void test_strbuf_00(void **state) char *p; assert_ptr_equal(buf.buf, NULL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); p = steal_strbuf_str(&buf); assert_ptr_equal(p, NULL); assert_ptr_equal(buf.buf, NULL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); assert_int_equal(append_strbuf_str(&buf, "moin"), 4); - assert_int_equal(get_strbuf_len(&buf), 4); - assert_in_range(buf.size, 5, SIZE_MAX); + assert_uint_equal(get_strbuf_len(&buf), 4); + assert_uint_in_range(buf.size, 5, SIZE_MAX); assert_string_equal(get_strbuf_str(&buf), "moin"); p = steal_strbuf_str(&buf); assert_string_equal(p, "moin"); free(p); assert_ptr_equal(buf.buf, NULL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); assert_int_equal(append_strbuf_str(&buf, NULL), -EINVAL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); assert_int_equal(append_strbuf_str(&buf, ""), 0); /* appending a 0-length string allocates memory */ - assert_in_range(buf.size, 1, SIZE_MAX); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_in_range(buf.size, 1, SIZE_MAX); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); p = steal_strbuf_str(&buf); assert_string_equal(p, ""); @@ -80,9 +84,9 @@ static void test_strbuf_00(void **state) assert_int_equal(append_strbuf_str__(&buf, "x", 0), 0); /* appending a 0-length string allocates memory */ - assert_in_range(buf.size, 1, SIZE_MAX); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_in_range(buf.size, 1, SIZE_MAX); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); } @@ -95,17 +99,17 @@ static void test_strbuf_alloc_err(void **state) mock_realloc = true; will_return(__wrap_realloc, NULL); assert_int_equal(append_strbuf_str(&buf, "moin"), -ENOMEM); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); mock_realloc = false; assert_int_equal(append_strbuf_str(&buf, "moin"), 4); sz = buf.size; - assert_in_range(sz, 5, SIZE_MAX); - assert_int_equal(buf.offs, 4); - assert_int_equal(get_strbuf_len(&buf), 4); + assert_uint_in_range(sz, 5, SIZE_MAX); + assert_uint_equal(buf.offs, 4); + assert_uint_equal(get_strbuf_len(&buf), 4); assert_string_equal(get_strbuf_str(&buf), "moin"); mock_realloc = true; @@ -114,22 +118,22 @@ static void test_strbuf_alloc_err(void **state) while ((rc = append_strbuf_str(&buf, " hello")) >= 0) { condlog(3, "%s", get_strbuf_str(&buf)); assert_int_equal(rc, 6); - assert_int_equal(get_strbuf_len(&buf), ofs + 6); + assert_uint_equal(get_strbuf_len(&buf), ofs + 6); assert_memory_equal(get_strbuf_str(&buf), "moin", 4); assert_string_equal(get_strbuf_str(&buf) + ofs, " hello"); ofs = get_strbuf_len(&buf); } assert_int_equal(rc, -ENOMEM); - assert_int_equal(buf.size, sz); - assert_int_equal(get_strbuf_len(&buf), ofs); + assert_uint_equal(buf.size, sz); + assert_uint_equal(get_strbuf_len(&buf), ofs); assert_memory_equal(get_strbuf_str(&buf), "moin", 4); assert_string_equal(get_strbuf_str(&buf) + ofs - 6, " hello"); reset_strbuf(&buf); assert_ptr_equal(buf.buf, NULL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); mock_realloc = false; @@ -158,7 +162,8 @@ static void test_strbuf_big(void **state) if (i % 1000 == 0) condlog(4, "%d", i); assert_int_equal(append_strbuf_str(&buf, big), sizeof(big) - 1); - assert_int_equal(get_strbuf_len(&buf), (sizeof(big) - 1) * (i + 1)); + assert_uint_equal(get_strbuf_len(&buf), + (sizeof(big) - 1) * (i + 1)); assert_memory_equal(get_strbuf_str(&buf), big, sizeof(big) - 1); assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - (sizeof(big) - 1), big); @@ -166,12 +171,12 @@ static void test_strbuf_big(void **state) bbig = steal_strbuf_str(&buf); assert_ptr_equal(buf.buf, NULL); - assert_int_equal(buf.size, 0); - assert_int_equal(buf.offs, 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(buf.size, 0); + assert_uint_equal(buf.offs, 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); - assert_int_equal(strlen(bbig), i * (sizeof(big) - 1)); + assert_uint_equal(strlen(bbig), i * (sizeof(big) - 1)); assert_memory_equal(bbig, big, sizeof(big) - 1); free(bbig); } @@ -190,23 +195,23 @@ static void test_strbuf_nul(void **state) greet[5] = '\0'; reset_strbuf(&buf); assert_int_equal(append_strbuf_str(&buf, greet), 5); - assert_int_equal(get_strbuf_len(&buf), 5); + assert_uint_equal(get_strbuf_len(&buf), 5); assert_string_equal(get_strbuf_str(&buf), "hello"); assert_int_equal(append_strbuf_str(&buf, greet), 5); - assert_int_equal(get_strbuf_len(&buf), 10); + assert_uint_equal(get_strbuf_len(&buf), 10); assert_string_equal(get_strbuf_str(&buf), "hellohello"); /* append_strbuf_str__() appends full memory, including NUL bytes */ reset_strbuf(&buf); assert_int_equal(append_strbuf_str__(&buf, greet, sizeof(greet) - 1), sizeof(greet) - 1); - assert_int_equal(get_strbuf_len(&buf), sizeof(greet) - 1); + assert_uint_equal(get_strbuf_len(&buf), sizeof(greet) - 1); assert_string_equal(get_strbuf_str(&buf), "hello"); assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!"); assert_int_equal(append_strbuf_str__(&buf, greet, sizeof(greet) - 1), sizeof(greet) - 1); assert_string_equal(get_strbuf_str(&buf), "hello"); - assert_int_equal(get_strbuf_len(&buf), 2 * (sizeof(greet) - 1)); + assert_uint_equal(get_strbuf_len(&buf), 2 * (sizeof(greet) - 1)); assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!"); } @@ -318,7 +323,7 @@ static void test_print_strbuf_3(void **state) s = get_strbuf_str(&buf); condlog(3, "%s", s); - assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1)); + assert_uint_equal(strlen(s), repeat * (sizeof(sentence) - 1)); for (i = 0; i < repeat; i++) assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1), sentence, sizeof(sentence) - 1), 0); @@ -340,7 +345,7 @@ static void test_print_strbuf_4(void **state) s = get_strbuf_str(&buf); condlog(3, "%s", s); - assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1)); + assert_uint_equal(strlen(s), repeat * (sizeof(sentence) - 1)); for (i = 0; i < repeat; i++) assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1), sentence, sizeof(sentence) - 1), 0); @@ -356,24 +361,24 @@ static void test_truncate_strbuf(void **state) assert_int_equal(truncate_strbuf(&buf, 0), -EFAULT); assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1); - assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1); + assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1); assert_string_equal(get_strbuf_str(&buf), str); assert_int_equal(truncate_strbuf(&buf, sizeof(str)), -ERANGE); - assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1); + assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1); assert_string_equal(get_strbuf_str(&buf), str); assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 1), 0); - assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1); + assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1); assert_string_equal(get_strbuf_str(&buf), str); assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 2), 0); - assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 2); + assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 2); assert_string_not_equal(get_strbuf_str(&buf), str); assert_memory_equal(get_strbuf_str(&buf), str, sizeof(str) - 2); assert_int_equal(truncate_strbuf(&buf, 5), 0); - assert_int_equal(get_strbuf_len(&buf), 5); + assert_uint_equal(get_strbuf_len(&buf), 5); assert_string_not_equal(get_strbuf_str(&buf), str); assert_string_equal(get_strbuf_str(&buf), "hello"); @@ -385,7 +390,7 @@ static void test_truncate_strbuf(void **state) assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1); sz1 = buf.size; - assert_in_range(get_strbuf_len(&buf), sz + 1, SIZE_MAX); + assert_uint_in_range(get_strbuf_len(&buf), sz + 1, SIZE_MAX); assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - (sizeof(str) - 1), str); assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf) + 1), @@ -393,20 +398,20 @@ static void test_truncate_strbuf(void **state) assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf)), 0); assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf) - (sizeof(str) - 1)), 0); - assert_in_range(get_strbuf_len(&buf), 1, sz); + assert_uint_in_range(get_strbuf_len(&buf), 1, sz); assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - (sizeof(str) - 1), str); - assert_int_equal(buf.size, sz1); + assert_uint_equal(buf.size, sz1); assert_int_equal(truncate_strbuf(&buf, 5), 0); - assert_int_equal(get_strbuf_len(&buf), 5); + assert_uint_equal(get_strbuf_len(&buf), 5); assert_string_equal(get_strbuf_str(&buf), "hello"); - assert_int_equal(buf.size, sz1); + assert_uint_equal(buf.size, sz1); assert_int_equal(truncate_strbuf(&buf, 0), 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); - assert_int_equal(buf.size, sz1); + assert_uint_equal(buf.size, sz1); } static void test_fill_strbuf(void **state) @@ -418,31 +423,31 @@ static void test_fill_strbuf(void **state) assert_int_equal(fill_strbuf(&buf, '+', -5), -EINVAL); assert_int_equal(fill_strbuf(&buf, '+', 0), 0); - assert_int_equal(get_strbuf_len(&buf), 0); + assert_uint_equal(get_strbuf_len(&buf), 0); assert_string_equal(get_strbuf_str(&buf), ""); assert_int_equal(fill_strbuf(&buf, '+', 1), 1); - assert_int_equal(get_strbuf_len(&buf), 1); + assert_uint_equal(get_strbuf_len(&buf), 1); assert_string_equal(get_strbuf_str(&buf), "+"); assert_int_equal(fill_strbuf(&buf, '-', 3), 3); - assert_int_equal(get_strbuf_len(&buf), 4); + assert_uint_equal(get_strbuf_len(&buf), 4); assert_string_equal(get_strbuf_str(&buf), "+---"); assert_int_equal(fill_strbuf(&buf, '\0', 3), 3); - assert_int_equal(get_strbuf_len(&buf), 7); + assert_uint_equal(get_strbuf_len(&buf), 7); assert_string_equal(get_strbuf_str(&buf), "+---"); truncate_strbuf(&buf, 4); assert_int_equal(fill_strbuf(&buf, '+', 4), 4); - assert_int_equal(get_strbuf_len(&buf), 8); + assert_uint_equal(get_strbuf_len(&buf), 8); assert_string_equal(get_strbuf_str(&buf), "+---++++"); reset_strbuf(&buf); assert_int_equal(fill_strbuf(&buf, 'x', 30000), 30000); - assert_int_equal(get_strbuf_len(&buf), 30000); + assert_uint_equal(get_strbuf_len(&buf), 30000); p = steal_strbuf_str(&buf); - assert_int_equal(strlen(p), 30000); + assert_uint_equal(strlen(p), 30000); for (i = 0; i < 30000; i++) assert_int_equal(p[i], 'x'); free(p); diff --git a/tests/sysfs.c b/tests/sysfs.c index 6dfa9f3f5..a3c0a7f71 100644 --- a/tests/sysfs.c +++ b/tests/sysfs.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021 SUSE LLC - * SPDX-License-Identifier: GPL-2.0-only */ #define _GNU_SOURCE @@ -9,8 +9,8 @@ #include #include #include -#include -#include +#include "cmocka-compat.h" +#include "mt-udev-wrap.h" #include #include #include "debug.h" @@ -22,9 +22,9 @@ #define TEST_FD 123 -char *__wrap_udev_device_get_syspath(struct udev_device *ud) +const char *__wrap_udev_device_get_syspath(struct udev_device *ud) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); return val; } @@ -33,8 +33,8 @@ int WRAP_OPEN(const char *pathname, int flags) { int ret; - check_expected(pathname); - check_expected(flags); + check_expected_ptr(pathname); + check_expected_int(flags); ret = mock_type(int); return ret; } @@ -42,12 +42,12 @@ int WRAP_OPEN(const char *pathname, int flags) ssize_t __wrap_read(int fd, void *buf, size_t count) { ssize_t ret; - char *val; + const char *val; - check_expected(fd); - check_expected(count); + check_expected_int(fd); + check_expected_uint(count); ret = mock_type(int); - val = mock_ptr_type(char *); + val = mock_ptr_type(const char *); if (ret >= (ssize_t)count) ret = count; if (ret >= 0 && val) { @@ -61,8 +61,8 @@ ssize_t __wrap_write(int fd, void *buf, size_t count) { ssize_t ret; - check_expected(fd); - check_expected(count); + check_expected_int(fd); + check_expected_uint(count); ret = mock_type(int); if (ret >= (ssize_t)count) ret = count; @@ -168,7 +168,7 @@ static void test_sagv_open_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); errno = ENOENT; wrap_will_return(WRAP_OPEN, -1); expect_condlog(3, "sysfs_attr_get_value__: attribute '/foo/bar' cannot be opened"); @@ -183,10 +183,10 @@ static void test_sagv_read_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_read, fd, TEST_FD); - expect_value(__wrap_read, count, sizeof(buf)); + expect_int_value(__wrap_read, fd, TEST_FD); + expect_uint_value(__wrap_read, count, sizeof(buf)); errno = EISDIR; will_return(__wrap_read, -1); will_return(__wrap_read, NULL); @@ -198,10 +198,10 @@ static void test_sagv_read_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/baz'"); expect_string(WRAP_OPEN, pathname, "/foo/baz"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_read, fd, TEST_FD); - expect_value(__wrap_read, count, sizeof(buf)); + expect_int_value(__wrap_read, fd, TEST_FD); + expect_uint_value(__wrap_read, count, sizeof(buf)); errno = EPERM; will_return(__wrap_read, -1); will_return(__wrap_read, NULL); @@ -219,15 +219,15 @@ static void _test_sagv_read(void **state, unsigned int bufsz) char input[] = "01234567"; unsigned int n, trunc; - assert_in_range(bufsz, 1, sizeof(buf)); + assert_uint_in_range(bufsz, 1, sizeof(buf)); memset(buf, '.', sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_read, fd, TEST_FD); - expect_value(__wrap_read, count, bufsz); + expect_int_value(__wrap_read, fd, TEST_FD); + expect_uint_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); will_return(__wrap_read, input); @@ -251,10 +251,10 @@ static void _test_sagv_read(void **state, unsigned int bufsz) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/baz'"); expect_string(WRAP_OPEN, pathname, "/foo/baz"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_read, fd, TEST_FD); - expect_value(__wrap_read, count, bufsz); + expect_int_value(__wrap_read, fd, TEST_FD); + expect_uint_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); will_return(__wrap_read, input); will_return(__wrap_close, 0); @@ -297,15 +297,15 @@ static void _test_sagv_read_zeroes(void **state, unsigned int bufsz) char input[] = { '\0','\0','\0','\0','\0','\0','\0','\0' }; unsigned int n; - assert_in_range(bufsz, 1, sizeof(buf)); + assert_uint_in_range(bufsz, 1, sizeof(buf)); memset(buf, '.', sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_RDONLY); + expect_int_value(WRAP_OPEN, flags, O_RDONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_read, fd, TEST_FD); - expect_value(__wrap_read, count, bufsz); + expect_int_value(__wrap_read, fd, TEST_FD); + expect_uint_value(__wrap_read, count, bufsz); will_return(__wrap_read, sizeof(input) - 1); will_return(__wrap_read, input); @@ -387,7 +387,7 @@ static void test_sasv_open_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_WRONLY); + expect_int_value(WRAP_OPEN, flags, O_WRONLY); errno = EPERM; wrap_will_return(WRAP_OPEN, -1); expect_condlog(3, "sysfs_attr_set_value: attribute '/foo/bar' cannot be opened"); @@ -402,10 +402,10 @@ static void test_sasv_write_fail(void **state) will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_WRONLY); + expect_int_value(WRAP_OPEN, flags, O_WRONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_write, fd, TEST_FD); - expect_value(__wrap_write, count, sizeof(buf)); + expect_int_value(__wrap_write, fd, TEST_FD); + expect_uint_value(__wrap_write, count, sizeof(buf)); errno = EISDIR; will_return(__wrap_write, -1); expect_condlog(3, "sysfs_attr_set_value: write to /foo/bar failed:"); @@ -419,14 +419,14 @@ static void _test_sasv_write(void **state, unsigned int n_written) { char buf[8]; - assert_in_range(n_written, 0, sizeof(buf)); + assert_uint_in_range(n_written, 0, sizeof(buf)); will_return(__wrap_udev_device_get_syspath, "/foo"); expect_condlog(4, "open '/foo/bar'"); expect_string(WRAP_OPEN, pathname, "/foo/bar"); - expect_value(WRAP_OPEN, flags, O_WRONLY); + expect_int_value(WRAP_OPEN, flags, O_WRONLY); wrap_will_return(WRAP_OPEN, TEST_FD); - expect_value(__wrap_write, fd, TEST_FD); - expect_value(__wrap_write, count, sizeof(buf)); + expect_int_value(__wrap_write, fd, TEST_FD); + expect_uint_value(__wrap_write, count, sizeof(buf)); will_return(__wrap_write, n_written); if (n_written < sizeof(buf)) diff --git a/tests/test-lib.c b/tests/test-lib.c index e529711d4..ea99977ff 100644 --- a/tests/test-lib.c +++ b/tests/test-lib.c @@ -1,8 +1,8 @@ #include #include #include -#include -#include +#include "cmocka-compat.h" +#include "mt-udev-wrap.h" #include #include #include @@ -55,7 +55,7 @@ int WRAP_OPEN(const char *path, int flags, int mode) int __wrap_libmp_get_version(int which, unsigned int version[3]) { - unsigned int *vers = mock_ptr_type(unsigned int *); + const unsigned int *vers = mock_ptr_type(const unsigned int *); condlog(4, "%s: %d", __func__, which); memcpy(version, vers, 3 * sizeof(unsigned int)); @@ -82,7 +82,7 @@ struct udev_list_entry const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *udle) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s", __func__, val); return val; @@ -98,54 +98,67 @@ struct udev_device *__wrap_udev_device_unref(struct udev_device *ud) return ud; } -char *__wrap_udev_device_get_subsystem(struct udev_device *ud) +/* + * cmocka 2.0 uses "const void *" for the pointer argument. + * As we have to return char *, we must accept casting + * (const void *) to (char *) here. + */ +const char *__wrap_udev_device_get_subsystem(struct udev_device *ud) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s", __func__, val); return val; } -char *__wrap_udev_device_get_sysname(struct udev_device *ud) +const char *__wrap_udev_device_get_sysname(struct udev_device *ud) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s", __func__, val); return val; } -char *__wrap_udev_device_get_devnode(struct udev_device *ud) +const char *__wrap_udev_device_get_devnode(struct udev_device *ud) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s", __func__, val); return val; } -dev_t __wrap_udev_device_get_devnum(struct udev_device *ud) +const char * +__wrap_udev_device_get_sysattr_value(struct udev_device *ud, const char *attr) { - condlog(5, "%s: %p", __func__, ud); - return makedev(17, 17); + const char *val = mock_ptr_type(const char *); + + condlog(5, "%s: %s->%s", __func__, attr, val); + return val; } -char *__wrap_udev_device_get_sysattr_value(struct udev_device *ud, - const char *attr) +const char * +__wrap_udev_device_get_property_value(struct udev_device *ud, const char *attr) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s->%s", __func__, attr, val); return val; } -char *__wrap_udev_device_get_property_value(struct udev_device *ud, - const char *attr) +const void *__wrap_udev_device_get_parent(struct udev_device *ud) { - char *val = mock_ptr_type(char *); + const void *val = mock_ptr_type(const void *); - condlog(5, "%s: %s->%s", __func__, attr, val); + condlog(5, "%s: %p", __func__, val); return val; } +dev_t __wrap_udev_device_get_devnum(struct udev_device *ud) +{ + condlog(5, "%s: %p", __func__, ud); + return makedev(17, 17); +} + int __wrap_sysfs_get_size(struct path *pp, unsigned long long *sz) { *sz = 12345678UL; @@ -166,19 +179,10 @@ void *__wrap_udev_device_get_parent_with_subsystem_devtype( return type; } -void *__wrap_udev_device_get_parent(struct udev_device *ud) -{ - char *val = mock_ptr_type(void *); - - condlog(5, "%s: %p", __func__, val); - return val; -} - -ssize_t __wrap_sysfs_attr_get_value(struct udev_device *dev, - const char *attr_name, - char *value, size_t sz) +size_t __wrap_sysfs_attr_get_value(struct udev_device *dev, + const char *attr_name, char *value, size_t sz) { - char *val = mock_ptr_type(char *); + const char *val = mock_ptr_type(const char *); condlog(5, "%s: %s", __func__, val); strlcpy(value, val, sz); @@ -193,7 +197,7 @@ ssize_t __wrap_sysfs_bin_attr_get_value(struct udev_device *dev, static const char serial[] = "mptest_serial"; assert_string_equal(attr_name, "vpd_pg80"); - assert_in_range(sz, sizeof(serial) + 3, INT_MAX); + assert_uint_in_range(sz, sizeof(serial) + 3, INT_MAX); memset(buf, 0, sizeof(serial) + 3); buf[1] = 0x80; put_unaligned_be16(sizeof(serial) - 1, &buf[2]); @@ -238,8 +242,8 @@ int __wrap_ioctl(int fd, unsigned long request, void *param) if (hdr->interface_id == 'S' && hdr->cmdp[0] == 0x12 && (hdr->cmdp[1] & 1) == 1 && hdr->cmdp[2] == HP3PAR_VPD) { - assert_in_range(hdr->dxfer_len, - sizeof(vpd_data) + 3, INT_MAX); + assert_uint_in_range(hdr->dxfer_len, + sizeof(vpd_data) + 3, INT_MAX); memset(buf, 0, hdr->dxfer_len); buf[1] = HP3PAR_VPD; put_unaligned_be16(sizeof(vpd_data), &buf[2]); @@ -292,12 +296,13 @@ static void mock_sysfs_pathinfo(const struct mocked_path *mp) /* sysfs_get_tgt_nodename */ will_return(__wrap_udev_device_get_sysattr_value, NULL); + will_return(__wrap_udev_device_get_subsystem, NULL); will_return(__wrap_udev_device_get_parent, NULL); will_return(__wrap_udev_device_get_parent, NULL); will_return(__wrap_udev_device_get_sysname, "nofibre"); will_return(__wrap_udev_device_get_sysname, "noiscsi"); - will_return(__wrap_udev_device_get_parent, NULL); will_return(__wrap_udev_device_get_sysname, "ata25"); + will_return(__wrap_udev_device_get_parent, NULL); } /* @@ -328,7 +333,7 @@ void mock_pathinfo(int mask, const struct mocked_path *mp) (mask & DI_BLACKLIST && mask & DI_SYSFS)) return; - /* path_offline */ + /* path_sysfs_state */ will_return(__wrap_udev_device_get_subsystem, "scsi"); will_return(__wrap_sysfs_attr_get_value, "running"); diff --git a/tests/test-log.c b/tests/test-log.c index 9ace47832..b907e7ba0 100644 --- a/tests/test-log.c +++ b/tests/test-log.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "log.h" #include "test-log.h" #include "debug.h" @@ -15,16 +15,16 @@ void __wrap_dlog (int prio, const char * fmt, ...) { char buff[MAX_MSG_SIZE]; va_list ap; - char *expected; + const char *expected; va_start(ap, fmt); vsnprintf(buff, MAX_MSG_SIZE, fmt, ap); va_end(ap); fprintf(stderr, "%s(%d): %s", __func__, prio, buff); - expected = mock_ptr_type(char *); + expected = mock_ptr_type(const char *); if (memcmp(expected, buff, strlen(expected))) fprintf(stderr, "%s(expected): %s", __func__, expected); - check_expected(prio); + check_expected_int(prio); assert_memory_equal(buff, expected, strlen(expected)); } @@ -32,6 +32,6 @@ void expect_condlog(int prio, char *string) { if (prio > MAX_VERBOSITY || prio > libmp_verbosity) return; - expect_value(__wrap_dlog, prio, prio); + expect_int_value(__wrap_dlog, prio, prio); will_return(__wrap_dlog, string); } diff --git a/tests/uevent.c b/tests/uevent.c index 39f5dbdd6..044d4905d 100644 --- a/tests/uevent.c +++ b/tests/uevent.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "list.h" #include "uevent.h" diff --git a/tests/unaligned.c b/tests/unaligned.c index e43b64d4b..252f8f4b6 100644 --- a/tests/unaligned.c +++ b/tests/unaligned.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include "unaligned.h" #define SIZE 16 @@ -40,13 +40,13 @@ static int teardown(void **state) uint8_t *p = *state + SIZE; \ uint64_t u; \ \ - assert_in_range(len, 1, SIZE); \ - assert_in_range(offset + len, 1, SIZE); \ + assert_int_in_range(len, 1, SIZE); \ + assert_int_in_range(offset + len, 1, SIZE); \ memset(c, 0, 2 * SIZE); \ memcpy(c + offset, memory, len); \ \ u = get_unaligned_be##bits(c + offset); \ - assert_int_equal(u, intval##bits); \ + assert_uint_equal(u, intval##bits); \ put_unaligned_be##bits(u, p + offset); \ assert_memory_equal(c + offset, p + offset, len); \ } diff --git a/tests/util.c b/tests/util.c index da192ba61..9f4a5d900 100644 --- a/tests/util.c +++ b/tests/util.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include #include "util.h" @@ -198,13 +198,13 @@ static uint64_t maybe_swap(uint64_t v) static void test_bitmask_1(void **state) { - struct bitfield *bf; + union bitfield *bf; uint64_t *arr; int i, j, k, m, b; bf = alloc_bitfield(BITARR_SZ * 64); assert_non_null(bf); - assert_int_equal(bf->len, BITARR_SZ * 64); + assert_uint_equal(bf->len, BITARR_SZ * 64); arr = (uint64_t *)bf->bits; for (j = 0; j < BITARR_SZ; j++) { @@ -218,9 +218,10 @@ static void test_bitmask_1(void **state) b, j, k, arr[k]); #endif if (k == j) - assert_int_equal(maybe_swap(arr[j]), 1ULL << i); + assert_uint_equal(maybe_swap(arr[j]), + 1ULL << i); else - assert_int_equal(arr[k], 0ULL); + assert_uint_equal(arr[k], 0ULL); } for (m = 0; m < 64; m++) if (i == m) @@ -232,7 +233,7 @@ static void test_bitmask_1(void **state) clear_bit_in_bitfield(b, bf); assert_false(is_bit_set_in_bitfield(b, bf)); for (k = 0; k < BITARR_SZ; k++) - assert_int_equal(arr[k], 0ULL); + assert_uint_equal(arr[k], 0ULL); } } free(bf); @@ -240,13 +241,13 @@ static void test_bitmask_1(void **state) static void test_bitmask_2(void **state) { - struct bitfield *bf; + union bitfield *bf; uint64_t *arr; int i, j, k, m, b; bf = alloc_bitfield(BITARR_SZ * 64); assert_non_null(bf); - assert_int_equal(bf->len, BITARR_SZ * 64); + assert_uint_equal(bf->len, BITARR_SZ * 64); arr = (uint64_t *)bf->bits; for (j = 0; j < BITARR_SZ; j++) { @@ -264,13 +265,12 @@ static void test_bitmask_2(void **state) assert_true(is_bit_set_in_bitfield(b, bf)); for (k = 0; k < BITARR_SZ; k++) { if (k < j || (k == j && i == 63)) - assert_int_equal(arr[k], ~0ULL); + assert_uint_equal(arr[k], ~0ULL); else if (k > j) - assert_int_equal(arr[k], 0ULL); + assert_uint_equal(arr[k], 0ULL); else - assert_int_equal( - maybe_swap(arr[k]), - (1ULL << (i + 1)) - 1); + assert_uint_equal(maybe_swap(arr[k]), + (1ULL << (i + 1)) - 1); } } } @@ -289,13 +289,12 @@ static void test_bitmask_2(void **state) assert_false(is_bit_set_in_bitfield(b, bf)); for (k = 0; k < BITARR_SZ; k++) { if (k < j || (k == j && i == 63)) - assert_int_equal(arr[k], 0ULL); + assert_uint_equal(arr[k], 0ULL); else if (k > j) - assert_int_equal(arr[k], ~0ULL); + assert_uint_equal(arr[k], ~0ULL); else - assert_int_equal( - maybe_swap(arr[k]), - ~((1ULL << (i + 1)) - 1)); + assert_uint_equal(maybe_swap(arr[k]), + ~((1ULL << (i + 1)) - 1)); } } } @@ -307,7 +306,7 @@ static void test_bitmask_2(void **state) */ static void test_bitmask_len_0(void **state) { - struct bitfield *bf; + union bitfield *bf; bf = alloc_bitfield(0); assert_null(bf); @@ -329,28 +328,28 @@ static unsigned int maybe_swap_idx(unsigned int i) static void _test_bitmask_small(unsigned int n) { - struct bitfield *bf; + union bitfield *bf; uint32_t *arr; unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i; assert_true(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8); - assert_in_range(n, 1, 64); + assert_uint_in_range(n, 1, 64); bf = alloc_bitfield(n); assert_non_null(bf); - assert_int_equal(bf->len, n); + assert_uint_equal(bf->len, n); arr = (uint32_t *)bf->bits; for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n + 1, bf); for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n, bf); for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n - 1, bf); for (i = 0; i < size; i++) { @@ -359,58 +358,58 @@ static void _test_bitmask_small(unsigned int n) unsigned int i1 = maybe_swap_idx(i); if (i == k) - assert_int_equal(arr[i1], 1UL << j); + assert_uint_equal(arr[i1], 1UL << j); else - assert_int_equal(arr[i1], 0); + assert_uint_equal(arr[i1], 0); } clear_bit_in_bitfield(n - 1, bf); for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(0, bf); - assert_int_equal(arr[maybe_swap_idx(0)], 1); + assert_uint_equal(arr[maybe_swap_idx(0)], 1); for (i = 1; i < size; i++) - assert_int_equal(arr[maybe_swap_idx(i)], 0); + assert_uint_equal(arr[maybe_swap_idx(i)], 0); free(bf); } static void _test_bitmask_small_2(unsigned int n) { - struct bitfield *bf; + union bitfield *bf; uint32_t *arr; unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i; - assert_in_range(n, 65, 128); + assert_uint_in_range(n, 65, 128); bf = alloc_bitfield(n); assert_non_null(bf); - assert_int_equal(bf->len, n); + assert_uint_equal(bf->len, n); arr = (uint32_t *)bf->bits; for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n + 1, bf); for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n, bf); for (i = 0; i < size; i++) - assert_int_equal(arr[i], 0); + assert_uint_equal(arr[i], 0); set_bit_in_bitfield(n - 1, bf); - assert_int_equal(arr[0], 0); + assert_uint_equal(arr[0], 0); for (i = 0; i < size; i++) { unsigned int k = (n - 1) / 32; unsigned int j = (n - 1) - k * 32; unsigned int i1 = maybe_swap_idx(i); if (i == k) - assert_int_equal(arr[i1], 1UL << j); + assert_uint_equal(arr[i1], 1UL << j); else - assert_int_equal(arr[i1], 0); + assert_uint_equal(arr[i1], 0); } set_bit_in_bitfield(0, bf); @@ -420,13 +419,13 @@ static void _test_bitmask_small_2(unsigned int n) unsigned int i1 = maybe_swap_idx(i); if (i == k && k == 0) - assert_int_equal(arr[i1], (1UL << j) | 1); + assert_uint_equal(arr[i1], (1UL << j) | 1); else if (i == k) - assert_int_equal(arr[i1], 1UL << j); + assert_uint_equal(arr[i1], 1UL << j); else if (i == 0) - assert_int_equal(arr[i1], 1); + assert_uint_equal(arr[i1], 1); else - assert_int_equal(arr[i1], 0); + assert_uint_equal(arr[i1], 0); } set_bit_in_bitfield(64, bf); @@ -436,13 +435,13 @@ static void _test_bitmask_small_2(unsigned int n) unsigned int i1 = maybe_swap_idx(i); if (i == k && (k == 0 || k == 2)) - assert_int_equal(arr[i1], (1UL << j) | 1); + assert_uint_equal(arr[i1], (1UL << j) | 1); else if (i == k) - assert_int_equal(arr[i1], 1UL << j); + assert_uint_equal(arr[i1], 1UL << j); else if (i == 2 || i == 0) - assert_int_equal(arr[i1], 1); + assert_uint_equal(arr[i1], 1); else - assert_int_equal(arr[i1], 0); + assert_uint_equal(arr[i1], 0); } clear_bit_in_bitfield(0, bf); @@ -452,13 +451,13 @@ static void _test_bitmask_small_2(unsigned int n) unsigned int i1 = maybe_swap_idx(i); if (i == k && k == 2) - assert_int_equal(arr[i1], (1UL << j) | 1); + assert_uint_equal(arr[i1], (1UL << j) | 1); else if (i == k) - assert_int_equal(arr[i1], 1UL << j); + assert_uint_equal(arr[i1], 1UL << j); else if (i == 2) - assert_int_equal(arr[i1], 1); + assert_uint_equal(arr[i1], 1); else - assert_int_equal(arr[i1], 0); + assert_uint_equal(arr[i1], 0); } free(bf); @@ -568,7 +567,7 @@ static void test_strlcpy_0(void **state) int rc; rc = strlcpy(tst, src_str, 0); - assert_int_equal(rc, strlen(src_str)); + assert_uint_equal(rc, strlen(src_str)); assert_string_equal(tst, dst_str); } @@ -579,7 +578,7 @@ static void test_strlcpy_1(void **state) int rc; rc = strlcpy(tst, src_str, 1); - assert_int_equal(rc, strlen(src_str)); + assert_uint_equal(rc, strlen(src_str)); assert_int_equal(tst[0], '\0'); assert_string_equal(tst + 1, dst_str + 1); } @@ -591,7 +590,7 @@ static void test_strlcpy_2(void **state) int rc; rc = strlcpy(tst, src_str, 2); - assert_int_equal(rc, strlen(src_str)); + assert_uint_equal(rc, strlen(src_str)); assert_int_equal(tst[0], src_str[0]); assert_int_equal(tst[1], '\0'); assert_string_equal(tst + 2, dst_str + 2); @@ -605,7 +604,7 @@ static void test_strlcpy_3(void **state) rc = strlcpy(tst, src_str, sizeof(tst)); assert_int_equal(rc, strlen(src_str)); - assert_int_equal(sizeof(tst) - 1, strlen(tst)); + assert_uint_equal(sizeof(tst) - 1, strlen(tst)); assert_true(strncmp(tst, src_str, sizeof(tst) - 1) == 0); } @@ -709,7 +708,7 @@ static int test_strlcpy(void) static void prep_buf(char *buf, size_t size, const char *word) { memset(buf, FILL, size); - assert_in_range(strlen(word), 0, size - 1); + assert_uint_in_range(strlen(word), 0, size - 1); memcpy(buf, word, strlen(word) + 1); } @@ -899,7 +898,7 @@ static void test_strchop_nochop(void **state) { char hello[] = "hello"; - assert_int_equal(strchop(hello), 5); + assert_uint_equal(strchop(hello), 5); assert_string_equal(hello, "hello"); } @@ -907,7 +906,7 @@ static void test_strchop_newline(void **state) { char hello[] = "hello\n"; - assert_int_equal(strchop(hello), 5); + assert_uint_equal(strchop(hello), 5); assert_string_equal(hello, "hello"); } @@ -915,7 +914,7 @@ static void test_strchop_space(void **state) { char hello[] = " ello "; - assert_int_equal(strchop(hello), 5); + assert_uint_equal(strchop(hello), 5); assert_string_equal(hello, " ello"); } @@ -923,7 +922,7 @@ static void test_strchop_mix(void **state) { char hello[] = " el\no \t \n\n \t \n"; - assert_int_equal(strchop(hello), 5); + assert_uint_equal(strchop(hello), 5); assert_string_equal(hello, " el\no"); } @@ -931,7 +930,7 @@ static void test_strchop_blank(void **state) { char hello[] = " \t \n\n \t \n"; - assert_int_equal(strchop(hello), 0); + assert_uint_equal(strchop(hello), 0); assert_string_equal(hello, ""); } @@ -939,7 +938,7 @@ static void test_strchop_empty(void **state) { char hello[] = ""; - assert_int_equal(strchop(hello), 0); + assert_uint_equal(strchop(hello), 0); assert_string_equal(hello, ""); } diff --git a/tests/valid.c b/tests/valid.c index 3f15ce66b..d6c8ee93e 100644 --- a/tests/valid.c +++ b/tests/valid.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include "globals.c" @@ -35,7 +35,7 @@ bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid) assert_non_null(pp); assert_int_not_equal(strlen(pp->dev), 0); if (is_multipathed && set_wwid) - strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE); + strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE); return is_multipathed; } @@ -59,7 +59,7 @@ int __wrap_mpath_disconnect(int fd) struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) { bool passed = mock_type(bool); - assert_string_equal(sysname, mock_ptr_type(char *)); + assert_string_equal(sysname, mock_ptr_type(const char *)); if (passed) return &test_udev; return NULL; @@ -68,16 +68,16 @@ struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *u /* For devtype check */ const char *__wrap_udev_device_get_property_value(struct udev_device *udev_device, const char *property) { - check_expected(property); - return mock_ptr_type(char *); + check_expected_ptr(property); + return mock_ptr_type(const char *); } /* For the "hidden" check in pathinfo() */ const char *__wrap_udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) { - check_expected(sysattr); - return mock_ptr_type(char *); + check_expected_ptr(sysattr); + return mock_ptr_type(const char *); } /* For pathinfo() -> is_claimed_by_foreign() */ @@ -89,7 +89,7 @@ int __wrap_add_foreign(struct udev_device *udev_device) /* For is_device_used() */ const char *__wrap_udev_device_get_sysname(struct udev_device *udev_device) { - return mock_ptr_type(char *); + return mock_ptr_type(const char *); } /* called from pathinfo() */ @@ -121,7 +121,7 @@ int __wrap_sysfs_get_size(struct path *pp, unsigned long long * size) /* called in pathinfo() before filter_property() */ int __wrap_select_getuid(struct config *conf, struct path *pp) { - pp->uid_attribute = mock_ptr_type(char *); + pp->uid_attribute = mock_ptr_type(const char *); return 0; } @@ -131,7 +131,7 @@ int __wrap_pathinfo(struct path *pp, struct config *conf, int mask) { int ret = mock_type(int); - assert_string_equal(pp->dev, mock_ptr_type(char *)); + assert_string_equal(pp->dev, mock_ptr_type(const char *)); assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST); if (ret == PATHINFO_REAL) { /* for test_filter_property() */ @@ -139,7 +139,7 @@ int __wrap_pathinfo(struct path *pp, struct config *conf, int mask) return ret; } else if (ret == PATHINFO_OK) { pp->uid_attribute = "ID_TEST"; - strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE); + strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE); } else memset(pp->wwid, 0, WWID_SIZE); return ret; @@ -156,20 +156,20 @@ int __wrap_filter_property(struct config *conf, struct udev_device *udev, int __wrap_is_failed_wwid(const char *wwid) { int ret = mock_type(int); - assert_string_equal(wwid, mock_ptr_type(char *)); + assert_string_equal(wwid, mock_ptr_type(const char *)); return ret; } const char *__wrap_udev_device_get_syspath(struct udev_device *udevice) { - return mock_ptr_type(char *); + return mock_ptr_type(const char *); } int __wrap_check_wwids_file(char *wwid, int write_wwid) { bool passed = mock_type(bool); assert_int_equal(write_wwid, 0); - assert_string_equal(wwid, mock_ptr_type(char *)); + assert_string_equal(wwid, mock_ptr_type(const char *)); if (passed) return 0; else @@ -180,7 +180,7 @@ int __wrap_dm_find_map_by_wwid(const char *wwid, char *name, struct dm_info *dmi) { int ret = mock_type(int); - assert_string_equal(wwid, mock_ptr_type(char *)); + assert_string_equal(wwid, mock_ptr_type(const char *)); return ret; } diff --git a/tests/vpd.c b/tests/vpd.c index e3212e614..f9e22cd87 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) 2019 Martin Wilck, SUSE Linux GmbH, Nuremberg */ #define _GNU_SOURCE @@ -13,7 +13,7 @@ #include #include #include -#include +#include "cmocka-compat.h" #include #include "unaligned.h" #include "debug.h" @@ -63,12 +63,12 @@ int WRAP_IOCTL(int fd, unsigned long request, void *param) { int len; struct sg_io_hdr *io_hdr; - unsigned char *val; + const unsigned char *val; len = mock(); io_hdr = (struct sg_io_hdr *)param; - assert_in_range(len, 0, io_hdr->dxfer_len); - val = mock_ptr_type(unsigned char *); + assert_int_in_range(len, 0, io_hdr->dxfer_len); + val = mock_ptr_type(const unsigned char *); io_hdr->status = 0; memcpy(io_hdr->dxferp, val, len); return 0; @@ -260,10 +260,22 @@ static int create_scsi_string_desc(unsigned char *desc, desc[1] = 8; desc[2] = 0; - assert_in_range(type, STR_EUI, STR_IQN); + assert_int_in_range(type, STR_EUI, STR_IQN); assert_true(maxlen % 4 == 0); +#if defined(__GNUC__) && __GNUC__ >= 15 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation=" + /* + * This is called from test_vpd_str_XYZ() functions, where + * we deliberately use the overlong input string test_id. + * So format truncation is expected here. + */ +#endif len = snprintf((char *)(desc + 4), maxlen, "%s%s", str_prefix[type], id); +#if defined(__GNUC__) && __GNUC__ >= 15 +#pragma GCC diagnostic pop +#endif if (len > maxlen) len = maxlen; /* zero-pad */ @@ -354,7 +366,7 @@ static void assert_correct_wwid(const char *test, } /* check matching length, and length of WWID string */ assert_int_equal(expected, returned); - assert_int_equal(returned, strlen(wwid)); + assert_uint_equal(returned, strlen(wwid)); /* check expected string value up to expected length */ for (i = 0; i < returned - ofs; i++) assert_int_equal(wwid[ofs + i], @@ -451,7 +463,6 @@ static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \ int n, ret; \ int exp_len; \ int type = typ & STR_MASK; \ - \ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \ 8, typ, len); \ exp_len = len - strlen(str_prefix[type]); \