Imported Upstream version 0.9.1 upstream/0.9.1
authorJinWang An <jinwang.an@samsung.com>
Fri, 13 Jan 2023 06:40:25 +0000 (15:40 +0900)
committerJinWang An <jinwang.an@samsung.com>
Fri, 13 Jan 2023 06:40:25 +0000 (15:40 +0900)
131 files changed:
.github/workflows/abi.yaml
.github/workflows/build-and-unittest.yaml
.github/workflows/coverity.yaml
.github/workflows/foreign.yaml
.github/workflows/native.yaml
.gitignore
Makefile
Makefile.inc
README.alua [deleted file]
README.md
kpartx/crc32.c
kpartx/gpt.c
kpartx/kpartx_id
libdmmp/Makefile
libdmmp/libdmmp_path.c
libmpathcmd/mpath_cmd.h
libmpathpersist/Makefile
libmpathpersist/mpath_persist_int.c
libmpathutil/Makefile [new file with mode: 0644]
libmpathutil/debug.c [new file with mode: 0644]
libmpathutil/debug.h [new file with mode: 0644]
libmpathutil/globals.c [new file with mode: 0644]
libmpathutil/globals.h [new file with mode: 0644]
libmpathutil/libmpathutil.version [new file with mode: 0644]
libmpathutil/log.c [new file with mode: 0644]
libmpathutil/log.h [new file with mode: 0644]
libmpathutil/log_pthread.c [new file with mode: 0644]
libmpathutil/log_pthread.h [new file with mode: 0644]
libmpathutil/msort.c [new file with mode: 0644]
libmpathutil/msort.h [new file with mode: 0644]
libmpathutil/parser.c [new file with mode: 0644]
libmpathutil/parser.h [new file with mode: 0644]
libmpathutil/strbuf.c [new file with mode: 0644]
libmpathutil/strbuf.h [new file with mode: 0644]
libmpathutil/time-util.c [new file with mode: 0644]
libmpathutil/time-util.h [new file with mode: 0644]
libmpathutil/util.c [new file with mode: 0644]
libmpathutil/util.h [new file with mode: 0644]
libmpathutil/uxsock.c [new file with mode: 0644]
libmpathutil/uxsock.h [new file with mode: 0644]
libmpathutil/vector.c [new file with mode: 0644]
libmpathutil/vector.h [new file with mode: 0644]
libmpathvalid/Makefile
libmpathvalid/mpath_valid.h
libmultipath/Makefile
libmultipath/alias.c
libmultipath/checkers/Makefile
libmultipath/checkers/directio.c
libmultipath/checkers/emc_clariion.c
libmultipath/checkers/hp_sw.c
libmultipath/checkers/rdac.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/debug.c [deleted file]
libmultipath/debug.h [deleted file]
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/dict.h
libmultipath/discovery.c
libmultipath/foreign/Makefile
libmultipath/foreign/nvme.c
libmultipath/hwtable.c
libmultipath/libmultipath.version
libmultipath/lock.h
libmultipath/log.c [deleted file]
libmultipath/log.h [deleted file]
libmultipath/log_pthread.c [deleted file]
libmultipath/log_pthread.h [deleted file]
libmultipath/nvme/linux/nvme.h
libmultipath/nvme/nvme.h
libmultipath/parser.c [deleted file]
libmultipath/parser.h [deleted file]
libmultipath/prioritizers/Makefile
libmultipath/prioritizers/alua_rtpg.c
libmultipath/prioritizers/alua_spc3.h
libmultipath/prioritizers/datacore.c
libmultipath/prioritizers/hds.c
libmultipath/prioritizers/path_latency.c
libmultipath/prkey.c
libmultipath/prkey.h
libmultipath/propsel.c
libmultipath/strbuf.c [deleted file]
libmultipath/strbuf.h [deleted file]
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/sysfs.c
libmultipath/sysfs.h
libmultipath/time-util.c [deleted file]
libmultipath/time-util.h [deleted file]
libmultipath/uevent.c
libmultipath/util.c [deleted file]
libmultipath/util.h [deleted file]
libmultipath/uxsock.c [deleted file]
libmultipath/uxsock.h [deleted file]
libmultipath/valid.h
libmultipath/vector.c [deleted file]
libmultipath/vector.h [deleted file]
libmultipath/version.h
libmultipath/wwids.c
mpathpersist/Makefile
mpathpersist/main.c
multipath/Makefile
multipath/main.c
multipath/multipath.conf.5
multipath/multipath.rules
multipathd/Makefile
multipathd/callbacks.c [new file with mode: 0644]
multipathd/cli.c
multipathd/cli.h
multipathd/cli_handlers.c
multipathd/fpin_handlers.c
multipathd/main.c
multipathd/multipathc.8 [new file with mode: 0644]
multipathd/multipathc.c [new file with mode: 0644]
multipathd/multipathd.8
multipathd/uxclnt.c
multipathd/uxlsnr.c
multipathd/uxlsnr.h
multipathd/waiter.c
tests/Makefile
tests/directio.c
tests/hwtable.c
tests/mpathvalid.c
tests/pgpolicy.c
tests/sysfs.c [new file with mode: 0644]
tests/test-lib.c
tests/test-log.c
tests/valid.c
third-party/valgrind/valgrind.h

index 0a40104a87526c6b41da3e1bfc9049ea99b2e44a..89b971cdf56533837e775f89d1f7d1ad71bf783f 100644 (file)
@@ -30,7 +30,7 @@ jobs:
           sudo apt-get install --yes gcc
           gcc make pkg-config abigail-tools
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
-          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: create ABI
         run: make -O -j$(grep -c ^processor /proc/cpuinfo) abi.tar.gz
       - name: save ABI
index 7ff45842877435c3a0449352f82e4224ada60df9..a5a0717d973003122a8485b94afe91b519e4ca06 100644 (file)
@@ -10,6 +10,9 @@ on:
 jobs:
   bionic:
     runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        rl: ['', 'libreadline', 'libedit']
     steps:
       - uses: actions/checkout@v2
       - name: mpath
@@ -27,9 +30,9 @@ jobs:
           sudo apt-get install --yes gcc
           make perl-base pkg-config valgrind
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
-          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: build
-        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
       - name: test
         run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
       - name: valgrind-test
@@ -42,6 +45,9 @@ jobs:
         run: sudo make DIO_TEST_DEV=/dev/zram$ZRAM test
   focal-gcc10:
     runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        rl: ['', 'libreadline', 'libedit']
     steps:
       - uses: actions/checkout@v2
       - name: mpath
@@ -55,11 +61,11 @@ jobs:
           sudo apt-get install --yes gcc-10
           make perl-base pkg-config valgrind
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
-          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: set CC
         run: echo CC=gcc-10 >> $GITHUB_ENV
       - name: build
-        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
       - name: test
         run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
       - name: valgrind-test
@@ -72,6 +78,9 @@ jobs:
         run: sudo make DIO_TEST_DEV=/dev/ram0 test
   focal-clang10:
     runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        rl: ['', 'libreadline', 'libedit']
     steps:
       - uses: actions/checkout@v2
       - name: mpath
@@ -85,11 +94,11 @@ jobs:
           sudo apt-get install --yes clang
           make perl-base pkg-config valgrind
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
-          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: set CC
         run: echo CC=clang >> $GITHUB_ENV
       - name: build
-        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
       - name: test
         run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
       - name: valgrind-test
index a8b56d43cbb76e9901e89f7401ca912401d03eb6..3c6b3824bbb081d0c2af735bbdd24bc6a5ae25a3 100644 (file)
@@ -15,7 +15,7 @@ jobs:
           sudo apt-get install --yes
           gcc make pkg-config
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
-          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: download coverity
         run: >
           curl -o cov-analysis-linux64.tar.gz
index e9ffd3d8161ca5d58e9c3822b8923036610cf81b..bd4e9c12a9942938bc278e0c21997a694228e819 100644 (file)
@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        os: [buster]
+        os: [bullseye]
         arch: ['ppc64le', 'aarch64', 's390x']
     container: mwilck/multipath-build-${{ matrix.os }}-${{ matrix.arch }}
     steps:
@@ -31,7 +31,9 @@ jobs:
         run: >
           tar cfv binaries.tar
           Makefile*
-          libmpathcmd/*.so* libmultipath/*.so*
+          libmpathcmd/*.so* libmultipath/*.so* libmpathutil/*.so*
+          libmultipath/checkers/*.so libmultipath/prioritizers/*.so
+          libmultipath/foreign/*.so
           tests/lib tests/*-test tests/Makefile tests/*.so*
       - uses: actions/upload-artifact@v1
         if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
@@ -44,7 +46,7 @@ jobs:
     needs: build
     strategy:
       matrix:
-        os: [buster]
+        os: [bullseye]
         arch: ['ppc64le', 'aarch64', 's390x']
     steps:
       - name: get binaries
@@ -56,7 +58,7 @@ jobs:
       - name: enable foreign arch
         run: sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
       - name: run tests
-        # Github actions doesn't support referencing docker images with
+        # GitHub actions doesn't support referencing docker images with
         # context variables. Workaround: use mosteo-actions/docker-run action
         # See https://github.community/t/expressions-in-docker-uri/16271
         uses: mosteo-actions/docker-run@v1
index 19c9e297a9d9bb65a1a01e499ec54ee4aa53972f..8b5992091f797b1b9bee75333b432ce2d6d2695c 100644 (file)
@@ -12,25 +12,34 @@ jobs:
     runs-on: ubuntu-20.04
     strategy:
       matrix:
-        os: [buster, jessie, bullseye, fedora-35]
+        os: [buster, jessie, bullseye, fedora-36]
         arch: ['', '-i386']
         exclude:
-          - os: fedora-34
-            arch: '-i386'
-          - os: fedora-35
+          - os: fedora-36
             arch: '-i386'
     container: mwilck/multipath-build-${{ matrix.os }}${{ matrix.arch }}
     steps:
       - name: checkout
         uses: actions/checkout@v1
       - name: build and test
+        if: ${{ matrix.os != 'jessie' }}
         run: make test
+      - name: build and test (jessie)
+        # On jessie, we use libreadline 5 (no licensing issue)
+        if: ${{ matrix.os == 'jessie' }}
+        run: make READLINE=libreadline test
       - name: clean
         run: make clean
       - name: clang
+        if: ${{ matrix.os != 'jessie' }}
         env:
           CC: clang
         run: make test
+      - name: clang (jessie)
+        if: ${{ matrix.os == 'jessie' }}
+        env:
+          CC: clang
+        run: make READLINE=libreadline test
 
   rolling:
     runs-on: ubuntu-20.04
@@ -58,12 +67,13 @@ jobs:
           libjson-c-dev
           liburcu-dev
           libcmocka-dev
+          libedit-dev
       - name: dependencies-alpine
         if: ${{ matrix.os == 'alpine' }}
         run: >
           apk add make gcc clang cmocka
           musl-dev lvm2-dev libaio-dev readline-dev ncurses-dev eudev-dev
-          userspace-rcu-dev json-c-dev cmocka-dev
+          userspace-rcu-dev json-c-dev cmocka-dev libedit-dev
       - name: dependencies-fedora
         if: ${{ matrix.os == 'fedora:rawhide' }}
         run: >
@@ -78,6 +88,7 @@ jobs:
           userspace-rcu-devel
           json-c-devel
           libcmocka-devel
+          libedit-devel
       - name: checkout
         uses: actions/checkout@v1
       - name: build and test
index 8e09f952e901794f633347c02fc23811d1299c56..821c3e6721dd02d49a347e5a2f75d2e4adabf803 100644 (file)
@@ -13,6 +13,7 @@ cscope.out
 kpartx/kpartx
 multipath/multipath
 multipathd/multipathd
+multipathd/multipathc
 mpathpersist/mpathpersist
 abi.tar.gz
 abi
@@ -32,3 +33,5 @@ tests/*.out
 tests/*.vgr
 libmultipath/nvme-ioctl.c
 libmultipath/nvme-ioctl.h
+*/*-nv.version
+reference-abi
index 82e0ea37c2a75cc007490a22c7a52a2bad1b577e..27b4641fe368ca1c7a576986359d1105925447f9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@
 
 LIB_BUILDDIRS := \
        libmpathcmd \
+       libmpathutil \
        libmultipath \
        libmpathpersist \
        libmpathvalid
@@ -25,7 +26,7 @@ BUILDDIRS := $(LIB_BUILDDIRS) \
 
 BUILDDIRS.clean := $(BUILDDIRS:=.clean) tests.clean
 
-.PHONY:        $(BUILDDIRS) $(BUILDDIRS:=.uninstall) $(BUILDDIRS:=.install) $(BUILDDIRS:=.clean) $(LIB_BUILDDIRS:=.abi)
+.PHONY:        $(BUILDDIRS)
 
 all:   $(BUILDDIRS)
 
@@ -53,7 +54,7 @@ abi.tar.gz:   abi
 abi-test:      abi reference-abi $(wildcard abi/*.abi)
        @err=0; \
        for lib in abi/*.abi; do \
-           diff=$$(abidiff "reference-$$lib" "$$lib") || { \
+           diff=$$(abidiff --redundant "reference-$$lib" "$$lib") || { \
                err=1; \
                echo "==== ABI differences in for $$lib ===="; \
                echo "$$diff"; \
@@ -72,7 +73,8 @@ compile_commands.json: Makefile Makefile.inc $(BUILDDIRS:=/Makefile)
        $(MAKE) clean
        bear -- $(MAKE)
 
-libmultipath libdmmp: libmpathcmd
+libmpathutil libdmmp: libmpathcmd
+libmultipath: libmpathutil
 libmpathpersist libmpathvalid multipath multipathd: libmultipath
 libmultipath/prioritizers libmultipath/checkers libmultipath/foreign: libmultipath
 mpathpersist multipathd:  libmpathpersist
@@ -84,7 +86,7 @@ libmultipath/checkers.install \
 $(BUILDDIRS.clean):
        $(MAKE) -C ${@:.clean=} clean
 
-$(BUILDDIRS:=.install):
+%.install:     %
        $(MAKE) -C ${@:.install=} install
 
 $(BUILDDIRS:=.uninstall):
@@ -93,13 +95,13 @@ $(BUILDDIRS:=.uninstall):
 clean: $(BUILDDIRS.clean)
        rm -rf abi abi.tar.gz abi-test compile_commands.json
 
-install: all $(BUILDDIRS:=.install)
+install: $(BUILDDIRS:=.install)
 uninstall: $(BUILDDIRS:=.uninstall)
 
 test-progs:    all
        $(MAKE) -C tests progs
 
-test:  test-progs
+test:  all
        $(MAKE) -C tests all
 
 valgrind-test: all
index bcd2212a2db0716ec7d4a8eb8f51caf8f7d9fda5..6399beb2bb440a230292346b77c086703e7a8178 100644 (file)
@@ -8,6 +8,11 @@
 #
 # Uncomment to disable dmevents polling support
 # ENABLE_DMEVENTS_POLL = 0
+#
+# 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
+READLINE :=
 
 # List of scsi device handler modules to load on boot, e.g.
 # SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
@@ -81,6 +86,7 @@ libudevdir    = $(prefix)/$(SYSTEMDPATH)/udev
 udevrulesdir   = $(libudevdir)/rules.d
 modulesloaddir  = $(prefix)/$(SYSTEMDPATH)/modules-load.d
 multipathdir   = $(TOPDIR)/libmultipath
+mpathutildir   = $(TOPDIR)/libmpathutil
 man8dir                = $(prefix)/usr/share/man/man8
 man5dir                = $(prefix)/usr/share/man/man5
 man3dir                = $(prefix)/usr/share/man/man3
diff --git a/README.alua b/README.alua
deleted file mode 100644 (file)
index 5d2b1c6..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-This is a rough guide, consult your storage device manufacturer documentation.
-
-ALUA is supported in some devices, but usually it's disabled by default.
-To enable ALUA, the following options should be changed:
-
-- EMC CLARiiON/VNX:
-   "Failover Mode" should be changed to "4" or "Active-Active mode(ALUA)-failover mode 4"
-
-- HPE 3PAR, Primera, and Alletra 9000:
-   "Host:" should be changed to "Generic-ALUA Persona 2 (UARepLun, SESLun, ALUA)".
-
-- Promise VTrak/Vess:
-   "LUN Affinity" and "ALUA" should be changed to "Enable", "Redundancy Type"
-   must be "Active-Active".
-
-- LSI/Engenio/NetApp RDAC class, as NetApp SANtricity E/EF Series and OEM arrays:
-   "Select operating system:" should be changed to "Linux DM-MP (Kernel 3.10 or later)".
-
-- NetApp ONTAP:
-   To check ALUA state: "igroup show -v <igroup_name>", and to enable ALUA:
-   "igroup set <igroup_name> alua yes".
-
-- Huawei OceanStor:
-   "Host Access Mode" should be changed to "Asymmetric".
index f06f8ceaea1988a65b18653a028a255b972ac4b1..b05b1332069c7de48e3be9c11a891f6185e2eede 100644 (file)
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
 multipath-tools for Linux
 =========================
 
-
 https://github.com/opensvc/multipath-tools
 
 This package provides the following binaries to drive the Device Mapper multipathing driver:
@@ -42,14 +41,12 @@ Go to: https://github.com/opensvc/multipath-tools/tags
 Select a release-tag and then click on "zip" or "tar.gz".
 
 
-Source code
-===========
+Devel code
+==========
 
 To get latest devel code:
 
-    git clone https://github.com/opensvc/multipath-tools.git
-
-Github page: https://github.com/opensvc/multipath-tools
+    git clone -b queue https://github.com/openSUSE/multipath-tools
 
 
 Building multipath-tools
@@ -150,3 +147,46 @@ Refer to the individual source files for details.
 Source files which do not specify a licence are shipped under LGPL-2.0
 (see `LICENSES/LGPL-2.0`).
 
+
+ALUA
+====
+This is a rough guide, consult your storage device manufacturer documentation.
+
+ALUA is supported in some devices, but usually it's disabled by default.
+To enable ALUA, the following options should be changed:
+
+- EMC CLARiiON/VNX:
+   "Failover Mode" should be changed to "4" or "Active-Active mode(ALUA)-failover mode 4"
+
+- HPE 3PAR, Primera, and Alletra 9000:
+   "Host:" should be changed to "Generic-ALUA Persona 2 (UARepLun, SESLun, ALUA)".
+
+- Promise VTrak/Vess:
+   "LUN Affinity" and "ALUA" should be changed to "Enable", "Redundancy Type"
+   must be "Active-Active".
+
+- LSI/Engenio/NetApp RDAC class, as NetApp SANtricity E/EF Series and OEM arrays:
+   "Select operating system:" should be changed to "Linux DM-MP (Kernel 3.10 or later)".
+
+- NetApp ONTAP:
+   To check ALUA state: "igroup show -v <igroup_name>", and to enable ALUA:
+   "igroup set <igroup_name> alua yes".
+
+- Huawei OceanStor:
+   "Host Access Mode" should be changed to "Asymmetric".
+
+
+NVMe
+====
+To use Device Mapper/multipath-tools with NVMe devices,
+if the Native NVMe Multipath subsystem is enabled
+( "Y" in `/sys/module/nvme_core/parameters/multipath` ),
+it has to be disabled:
+
+`echo "options nvme_core multipath=N" > /etc/modprobe.d/01-nvme_core-mp.conf`,
+regenerate the initramfs (`dracut -f` or `update-initramfs`) and reboot.
+
+Check that it is disabled(N) with:
+`cat /sys/module/nvme_core/parameters/multipath`
+or
+`systool -m nvme_core -A multipath`
index e688f8e9479e59a603a3c373a4270fd24a2c60ac..df6c687804e9a64c38ebdc5edafa9d19369826b5 100644 (file)
@@ -290,7 +290,7 @@ uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t
  * the end, so we have to add 32 extra cycles shifting in zeros at the
  * end of every message,
  *
- * So the standard trick is to rearrage merging in the next_input_bit()
+ * So the standard trick is to rearrange merging in the next_input_bit()
  * until the moment it's needed.  Then the first 32 cycles can be precomputed,
  * and merging in the final 32 zero bits to make room for the CRC can be
  * skipped entirely.
index 34a910cf3125e62c1294f27280334ec3bcff9189..47d8743eabf7eaa1b55f451a2dda55311b95e1ca 100644 (file)
@@ -357,7 +357,7 @@ is_gpt_valid(int fd, uint64_t lba,
                        __le32_to_cpu((*gpt)->num_partition_entries) *
                        __le32_to_cpu((*gpt)->sizeof_partition_entry));
        if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
-               // printf("GUID Partitition Entry Array CRC check failed.\n");
+               // printf("GUID Partition Entry Array CRC check failed.\n");
                free(*gpt);
                *gpt = NULL;
                free(*ptes);
index c45db2f80489f925b17426821319378d5585d42e..18732e7cbbaa5582e82ecc7dcfdb34ff2082eecd 100755 (executable)
@@ -93,7 +93,7 @@ if [ -n "$dmdeps" ] ; then
 else
     echo "DM_TYPE=raid"
 fi
-if [[ $dmserial ]]; then
+if [ -n "$dmserial" ]; then
     echo "DM_SERIAL=$dmserial"
 fi
 
index 2e99b3e2a2787b60f30839d53addf705e0400a25..e45892503b7bf13fe281dad74a7a0f748dcb5267 100644 (file)
@@ -45,6 +45,7 @@ install:
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
        perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
+       $(INSTALL_PROGRAM) -d 755 $(DESTDIR)$(man3dir)
        $(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3
 
 uninstall:
index 47a2162ce466b4fac010d15f68e1240824cb73f4..21714b156092fc3bcd6b4980cd4d4f55753af039 100644 (file)
@@ -28,7 +28,7 @@
 #include "libdmmp_private.h"
 
 #define _DMMP_SHOW_PS_INDEX_BLK_NAME   0
-#define _DMMP_SHOW_PS_INDEX_SATAUS     1
+#define _DMMP_SHOW_PS_INDEX_STATUS     1
 #define _DMMP_SHOW_PS_INDEX_WWID       2
 #define _DMMP_SHOW_PS_INDEX_PGID       3
 
index 30838b02d3e7229588bd9fc0c60f1c02860698ae..0c293c718cfa17bd5f2ec977366dacd8b585ca56 100644 (file)
@@ -65,7 +65,7 @@ int mpath_connect(void);
 /*
  * DESCRIPTION:
  *     Disconnect from the multipathd daemon. This function must be
- *     run after after processing all the multipath commands.
+ *     run after processing all the multipath commands.
  *
  * RETURNS:
  *     0 on success. -1 on failure (with errno set).
index 9843d96ec20f56d3d6ec499cbe692ee8baa51d7d..4e1717ef38ad66c138a2a6517bff3d94e13be6d1 100644 (file)
@@ -5,10 +5,10 @@ DEVLIB = libmpathpersist.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT:= libmpathpersist.version
 
-CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
-LDFLAGS += -L$(multipathdir) -L$(mpathcmddir)
+CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathutildir) -I$(mpathpersistdir) -I$(mpathcmddir)
+LDFLAGS += -L$(multipathdir) -L$(mpathutildir) -L$(mpathcmddir)
 
-LIBDEPS += -lmultipath -lmpathcmd -ldevmapper -lpthread -ldl
+LIBDEPS += -lmultipath -lmpathutil -lmpathcmd -ldevmapper -lpthread -ldl
 
 OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o mpath_persist_int.o
 
index e34fc32dc24c4e306aa32cff3244e51610f105c1..6924b37998855f97580da0e22e4aaf1d66d52d59 100644 (file)
@@ -601,7 +601,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
                if (get_be64(mpp->reservation_key) &&
                        memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
                               &mpp->reservation_key, 8)){
-                       /*register with tarnsport id*/
+                       /*register with transport id*/
                        memset(pamp, 0, length);
                        pamp->trnptid_list[0] = pptr;
                        memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
diff --git a/libmpathutil/Makefile b/libmpathutil/Makefile
new file mode 100644 (file)
index 0000000..68b1c7d
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@opensvc.com>
+#
+include ../Makefile.inc
+
+SONAME = 0
+DEVLIB = libmpathutil.so
+LIBS = $(DEVLIB).$(SONAME)
+VERSION_SCRIPT := libmpathutil.version
+
+CPPFLAGS += -I. -I$(multipathdir) -I$(mpathcmddir)
+CFLAGS += $(LIB_CFLAGS) -D_GNU_SOURCE
+
+LIBDEPS += -lpthread -ldl -ludev -L$(mpathcmddir) -lmpathcmd
+
+ifdef SYSTEMD
+       CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+       ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
+               LIBDEPS += -lsystemd
+       else
+               LIBDEPS += -lsystemd-daemon
+       endif
+endif
+
+# object files referencing MULTIPATH_DIR or CONFIG_DIR
+# they need to be recompiled for unit tests
+
+# other object files
+OBJS := 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)
+
+make_static = $(shell sed '/^static/!s/^\([a-z]\{1,\} \)/static \1/' <$1 >$2)
+
+$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
+               -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS)
+
+$(DEVLIB): $(LIBS)
+       $(LN) $(LIBS) $@
+
+$(NV_VERSION_SCRIPT):  $(VERSION_SCRIPT)
+       @printf 'NOVERSION {\nglobal:\n' >$@
+       @grep -P '^[ \t]+[a-zA-Z_][a-zA-Z0-9_]*;' $< >>$@
+       @printf 'local:\n\t*;\n};\n' >>$@
+
+$(LIBS:%.so.$(SONAME)=%-nv.so):        $(OBJS) $(NV_VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
+               -Wl,--version-script=$(NV_VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS)
+
+abi:    $(LIBS:%.so.$(SONAME)=%-nv.abi)
+
+install: all
+       $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
+       $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
+       $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(libdir)
+       $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+
+uninstall:
+       $(RM) $(DESTDIR)$(syslibdir)/$(LIBS)
+       $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+
+clean: dep_clean
+       $(RM) core *.a *.o *.so *.so.* *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT)
+
+include $(wildcard $(OBJS:.o=.d))
+
+dep_clean:
+       $(RM) $(OBJS:.o=.d)
diff --git a/libmpathutil/debug.c b/libmpathutil/debug.c
new file mode 100644 (file)
index 0000000..510e15e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2005 Christophe Varoqui
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "log_pthread.h"
+#include <sys/types.h>
+#include <time.h>
+#include "../third-party/valgrind/drd.h"
+#include "vector.h"
+#include "config.h"
+#include "defaults.h"
+#include "debug.h"
+#include "time-util.h"
+#include "util.h"
+
+int logsink;
+int libmp_verbosity = DEFAULT_VERBOSITY;
+
+void dlog(int prio, const char * fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (logsink != LOGSINK_SYSLOG) {
+               if (logsink == LOGSINK_STDERR_WITH_TIME) {
+                       struct timespec ts;
+                       char buff[32];
+
+                       get_monotonic_time(&ts);
+                       safe_sprintf(buff, "%ld.%06ld",
+                                    (long)ts.tv_sec,
+                                    ts.tv_nsec/1000);
+                       fprintf(stderr, "%s | ", buff);
+               }
+               vfprintf(stderr, fmt, ap);
+       }
+       else
+               log_safe(prio + 3, fmt, ap);
+       va_end(ap);
+}
diff --git a/libmpathutil/debug.h b/libmpathutil/debug.h
new file mode 100644 (file)
index 0000000..705a5d7
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+void dlog (int prio, const char *fmt, ...)
+       __attribute__((format(printf, 2, 3)));
+
+
+#include <pthread.h>
+#include <stdarg.h>
+
+#include "log_pthread.h"
+
+extern int logsink;
+extern int libmp_verbosity;
+
+#ifndef MAX_VERBOSITY
+#define MAX_VERBOSITY 4
+#endif
+
+enum {
+       LOGSINK_STDERR_WITH_TIME = 0,
+       LOGSINK_STDERR_WITHOUT_TIME = -1,
+       LOGSINK_SYSLOG = 1,
+};
+
+#define condlog(prio, fmt, args...)                                    \
+       do {                                                            \
+               int __p = (prio);                                       \
+                                                                       \
+               if (__p <= MAX_VERBOSITY && __p <= libmp_verbosity)     \
+                       dlog(__p, fmt "\n", ##args);                    \
+       } while (0)
+#endif /* _DEBUG_H */
diff --git a/libmpathutil/globals.c b/libmpathutil/globals.c
new file mode 100644 (file)
index 0000000..0c9a5ba
--- /dev/null
@@ -0,0 +1,12 @@
+#include <stddef.h>
+#include <libudev.h>
+#include "globals.h"
+
+struct udev __attribute__((weak)) *udev;
+struct config __attribute__((weak)) *get_multipath_config(void)
+{
+       return NULL;
+}
+
+void __attribute__((weak)) put_multipath_config(void *p __attribute__((unused)))
+{}
diff --git a/libmpathutil/globals.h b/libmpathutil/globals.h
new file mode 100644 (file)
index 0000000..fab507e
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _GLOBALS_H
+#define _GLOBALS_H
+
+struct config;
+
+/**
+ * extern variable: udev
+ *
+ * A &struct udev instance used by libmultipath. libmultipath expects
+ * a valid, initialized &struct udev in this variable.
+ * An application can define this variable itself, in which case
+ * the applications's instance will take precedence.
+ * The application can initialize and destroy this variable by
+ * calling libmultipath_init() and libmultipath_exit(), respectively,
+ * whether or not it defines the variable itself.
+ * An application can initialize udev with udev_new() before calling
+ * libmultipath_init(), e.g. if it has to make libudev calls before
+ * libmultipath calls. If an application wants to keep using the
+ * udev variable after calling libmultipath_exit(), it should have taken
+ * an additional reference on it beforehand. This is the case e.g.
+ * after initializing udev with udev_new().
+ */
+extern struct udev *udev;
+
+/*
+ * libmultipath provides default implementations of
+ * get_multipath_config() and put_multipath_config().
+ * Applications using these should use init_config(file, NULL)
+ * to load the configuration, rather than load_config(file).
+ * Likewise, uninit_config() should be used for teardown, but
+ * using free_config() for that is supported, too.
+ * Applications can define their own {get,put}_multipath_config()
+ * functions, which override the library-internal ones, but
+ * could still call libmp_{get,put}_multipath_config().
+ */
+void put_multipath_config(void *);
+struct config *get_multipath_config(void);
+
+#endif
diff --git a/libmpathutil/libmpathutil.version b/libmpathutil/libmpathutil.version
new file mode 100644 (file)
index 0000000..95b169d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * libmultipath ABI (libmpathutil part)
+ *
+ * libmultipath doesn't have a stable ABI in the usual sense. In particular,
+ * the library does not attempt to ship different versions of the same symbol
+ * for backward compatibility.
+ *
+ * The ABI versioning only serves to avoid linking with a non-matching ABI, to
+ * cut down the set of exported symbols, and to describe it.
+ * The version string is LIBMULTIPATH_$MAJOR.$MINOR.$REL.
+ *
+ * Policy:
+ *
+ * * Bump $MAJOR for incompatible changes, like:
+ *   - symbols removed
+ *   - parameter list or return values changed for existing functions
+ *   - externally visible data structures changed in incompatible ways
+ *     (like offsets of previously existing struct members)
+ *   In this case, the new version doesn't inherit the previous versions,
+ *   because the new library doesn't provide the full previous ABI any more.
+ *   All predecessors are merged into the new version.
+ *
+ * * Bump $MINOR for compatible changes, like adding symbols.
+ *   The new version inherits the previous ones.
+ *
+ * * Bump $REL to describe deviations from upstream, e.g. in
+ *   multipath-tools packages shipped by distributions.
+ *   The new version inherits the previous ones.
+ */
+
+/* symbols referenced by multipath and multipathd */
+LIBMULTIPATH_16.0.0 {
+global:
+       alloc_strvec;
+       append_strbuf_str;
+       cleanup_charp;
+       cleanup_mutex;
+       cleanup_ucharp;
+       convert_dev;
+       dlog;
+       fill_strbuf;
+       find_slot;
+       free_scandir_result;
+       free_strvec;
+       get_monotonic_time;
+       get_multipath_config;
+       get_next_string;
+       get_strbuf_len;
+       get_strbuf_str;
+       is_quote;
+       libmp_verbosity;
+       log_thread_reset;
+       log_thread_start;
+       log_thread_stop;
+       logsink;
+       normalize_timespec;
+       print_strbuf;
+       pthread_cond_init_mono;
+       put_multipath_config;
+       recv_packet;
+       reset_strbuf;
+       send_packet;
+       set_max_fds;
+       setup_thread_attr;
+       strchop;
+       strlcpy;
+       timespeccmp;
+       timespecsub;
+       truncate_strbuf;
+       udev;
+       ux_socket_listen;
+       vector_alloc;
+       vector_alloc_slot;
+       vector_del_slot;
+       vector_free;
+       vector_reset;
+       vector_set_slot;
+
+local:
+       *;
+};
+
+/* symbols referenced internally by libmultipath */
+LIBMPATHUTIL_1.0 {
+       alloc_bitfield;
+       __append_strbuf_str;
+       append_strbuf_quoted;
+       basenamecpy;
+       cleanup_free_ptr;
+       devt2devname;
+       filepresent;
+       find_keyword;
+       free_keywords;
+       get_linux_version_code;
+       __get_strbuf_buf;
+       get_word;
+       _install_keyword;
+       install_sublevel;
+       install_sublevel_end;
+       keyword_alloc;
+       _log_bitfield_overflow;
+       log_safe;
+       msort;
+       parse_devt;
+       parse_prkey;
+       process_file;
+       safe_write;
+       set_value;
+       should_exit;
+       snprint_keyword;
+       steal_strbuf_str;
+       strlcat;
+       systemd_service_enabled;
+       validate_config_strvec;
+       vector_find_or_add_slot;
+       vector_insert_slot;
+       vector_move_up;
+       vector_sort;
+};
+
+LIBMPATHUTIL_1.1 {
+global:
+       cleanup_fd_ptr;
+} LIBMPATHUTIL_1.0;
diff --git a/libmpathutil/log.c b/libmpathutil/log.c
new file mode 100644 (file)
index 0000000..45b3ed8
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2005 Christophe Varoqui
+ * Copyright (c) 2005 Benjamin Marzinski, Redhat
+ * Copyright (c) 2005 Jun'ichi Nomura, NEC
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "log.h"
+#include "util.h"
+
+#define ALIGN(len, s) (((len)+(s)-1)/(s)*(s))
+
+struct logarea* la;
+static pthread_mutex_t logq_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#if LOGDBG
+static void dump_logarea (void)
+{
+       struct logmsg * msg;
+
+       logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n",
+               la->start, la->end);
+       logdbg(stderr, "|addr     |next     |prio|msg\n");
+
+       for (msg = (struct logmsg *)la->head; (void *)msg != la->tail;
+            msg = msg->next)
+               logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
+                               msg->prio, (char *)&msg->str);
+
+       logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
+                       msg->prio, (char *)&msg->str);
+
+       logdbg(stderr, "\n\n");
+}
+#endif
+
+static int logarea_init (int size)
+{
+       logdbg(stderr,"enter logarea_init\n");
+       la = (struct logarea *)calloc(1, sizeof(struct logarea));
+
+       if (!la)
+               return 1;
+
+       if (size < MAX_MSG_SIZE)
+               size = DEFAULT_AREA_SIZE;
+
+       la->start = calloc(1, size);
+       if (!la->start) {
+               free(la);
+               la = NULL;
+               return 1;
+       }
+
+       la->empty = 1;
+       la->end = la->start + size;
+       la->head = la->start;
+       la->tail = la->start;
+
+       la->buff = calloc(1, MAX_MSG_SIZE + sizeof(struct logmsg));
+
+       if (!la->buff) {
+               free(la->start);
+               free(la);
+               la = NULL;
+               return 1;
+       }
+       return 0;
+
+}
+
+int log_init(char *program_name, int size)
+{
+       int ret = 1;
+
+       logdbg(stderr,"enter log_init\n");
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
+       openlog(program_name, 0, LOG_DAEMON);
+       if (!la)
+               ret = logarea_init(size);
+
+       pthread_cleanup_pop(1);
+
+       return ret;
+}
+
+static void free_logarea (void)
+{
+       free(la->start);
+       free(la->buff);
+       free(la);
+       la = NULL;
+       return;
+}
+
+void log_close (void)
+{
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
+       if (la)
+               free_logarea();
+       closelog();
+
+       pthread_cleanup_pop(1);
+       return;
+}
+
+void log_reset (char *program_name)
+{
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
+       closelog();
+       openlog(program_name, 0, LOG_DAEMON);
+
+       pthread_cleanup_pop(1);
+}
+
+__attribute__((format(printf, 2, 0)))
+static int _log_enqueue(int prio, const char * fmt, va_list ap)
+{
+       int len, fwd;
+       char buff[MAX_MSG_SIZE];
+       struct logmsg * msg;
+       struct logmsg * lastmsg;
+
+       lastmsg = (struct logmsg *)la->tail;
+
+       if (!la->empty) {
+               fwd = sizeof(struct logmsg) +
+                     strlen((char *)&lastmsg->str) * sizeof(char) + 1;
+               la->tail += ALIGN(fwd, sizeof(void *));
+       }
+       vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
+       len = ALIGN(sizeof(struct logmsg) + strlen(buff) * sizeof(char) + 1,
+                   sizeof(void *));
+
+       /* not enough space on tail : rewind */
+       if (la->head <= la->tail && len > (la->end - la->tail)) {
+               logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail);
+               if (la->head == la->start ) {
+                       logdbg(stderr, "enqueue: can not rewind tail, drop msg\n");
+                       la->tail = lastmsg;
+                       return 1;  /* can't reuse */
+               }
+               la->tail = la->start;
+
+               if (la->empty)
+                       la->head = la->start;
+       }
+
+       /* not enough space on head : drop msg */
+       if (la->head > la->tail && len >= (la->head - la->tail)) {
+               logdbg(stderr, "enqueue: log area overrun, drop msg\n");
+
+               if (!la->empty)
+                       la->tail = lastmsg;
+
+               return 1;
+       }
+
+       /* ok, we can stage the msg in the area */
+       la->empty = 0;
+       msg = (struct logmsg *)la->tail;
+       msg->prio = prio;
+       memcpy((void *)&msg->str, buff, strlen(buff) + 1);
+       lastmsg->next = la->tail;
+       msg->next = la->head;
+
+       logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg, msg->next,
+               msg->prio, (char *)&msg->str);
+
+#if LOGDBG
+       dump_logarea();
+#endif
+       return 0;
+}
+
+int log_enqueue(int prio, const char *fmt, va_list ap)
+{
+       int ret = 1;
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+       if (la)
+               ret = _log_enqueue(prio, fmt, ap);
+       pthread_cleanup_pop(1);
+       return ret;
+}
+
+static int _log_dequeue(void *buff)
+{
+       struct logmsg * src = (struct logmsg *)la->head;
+       struct logmsg * dst = (struct logmsg *)buff;
+       struct logmsg * lst = (struct logmsg *)la->tail;
+
+       if (la->empty)
+               return 1;
+
+       int len = strlen((char *)&src->str) * sizeof(char) +
+                 sizeof(struct logmsg) + 1;
+
+       dst->prio = src->prio;
+       memcpy(dst, src,  len);
+
+       if (la->tail == la->head)
+               la->empty = 1; /* we purge the last logmsg */
+       else {
+               la->head = src->next;
+               lst->next = la->head;
+       }
+       logdbg(stderr, "dequeue: %p, %p, %i, %s\n",
+               (void *)src, src->next, src->prio, (char *)&src->str);
+
+       memset((void *)src, 0,  len);
+
+       return 0;
+}
+
+int log_dequeue(void *buff)
+{
+       int ret = 1;
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+       if (la)
+               ret = _log_dequeue(buff);
+       pthread_cleanup_pop(1);
+       return ret;
+}
+
+/*
+ * this one can block under memory pressure
+ */
+void log_syslog (void * buff)
+{
+       struct logmsg * msg = (struct logmsg *)buff;
+
+       syslog(msg->prio, "%s", (char *)&msg->str);
+}
diff --git a/libmpathutil/log.h b/libmpathutil/log.h
new file mode 100644 (file)
index 0000000..fa224e4
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef LOG_H
+#define LOG_H
+
+#define DEFAULT_AREA_SIZE 16384
+#define MAX_MSG_SIZE 256
+
+#ifndef LOGLEVEL
+#define LOGLEVEL 5
+#endif
+
+#if LOGDBG
+#define logdbg(file, fmt, args...) fprintf(file, fmt, ##args)
+#else
+#define logdbg(file, fmt, args...) do {} while (0)
+#endif
+
+struct logmsg {
+       short int prio;
+       void * next;
+       char str[0];
+};
+
+struct logarea {
+       int empty;
+       void * head;
+       void * tail;
+       void * start;
+       void * end;
+       char * buff;
+};
+
+extern struct logarea* la;
+
+int log_init (char * progname, int size);
+void log_close (void);
+void log_reset (char * progname);
+int log_enqueue (int prio, const char * fmt, va_list ap)
+       __attribute__((format(printf, 2, 0)));
+int log_dequeue (void *);
+void log_syslog (void *);
+void dump_logmsg (void *);
+
+#endif /* LOG_H */
diff --git a/libmpathutil/log_pthread.c b/libmpathutil/log_pthread.c
new file mode 100644 (file)
index 0000000..69b441b
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2005 Christophe Varoqui
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#include "log_pthread.h"
+#include "log.h"
+#include "lock.h"
+#include "util.h"
+
+static pthread_t log_thr;
+
+/* logev_lock must not be taken with logq_lock held */
+static pthread_mutex_t logev_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t logev_cond = PTHREAD_COND_INITIALIZER;
+
+static int logq_running;
+static int log_messages_pending;
+
+void log_safe (int prio, const char * fmt, va_list ap)
+{
+       bool running;
+
+       if (prio > LOG_DEBUG)
+               prio = LOG_DEBUG;
+
+       /*
+        * logev_lock protects logq_running. By holding it, we avoid a race
+        * with log_thread_stop() -> log_close(), which would free the logarea.
+        */
+       pthread_mutex_lock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       running = logq_running;
+
+       if (running) {
+               log_enqueue(prio, fmt, ap);
+
+               log_messages_pending = 1;
+               pthread_cond_signal(&logev_cond);
+       }
+       pthread_cleanup_pop(1);
+
+       if (!running)
+               vsyslog(prio, fmt, ap);
+}
+
+static void flush_logqueue (void)
+{
+       int empty;
+
+       do {
+               empty = log_dequeue(la->buff);
+               if (!empty)
+                       log_syslog(la->buff);
+       } while (empty == 0);
+}
+
+static void cleanup_log_thread(__attribute((unused)) void *arg)
+{
+       logdbg(stderr, "log thread exiting");
+       pthread_mutex_lock(&logev_lock);
+       logq_running = 0;
+       pthread_mutex_unlock(&logev_lock);
+}
+
+static void * log_thread (__attribute__((unused)) void * et)
+{
+       int running;
+
+       pthread_mutex_lock(&logev_lock);
+       running = logq_running;
+       if (!running)
+               logq_running = 1;
+       pthread_cond_signal(&logev_cond);
+       pthread_mutex_unlock(&logev_lock);
+       if (running)
+               /* already started */
+               return NULL;
+       pthread_cleanup_push(cleanup_log_thread, NULL);
+
+       mlockall(MCL_CURRENT | MCL_FUTURE);
+       logdbg(stderr,"enter log_thread\n");
+
+       while (1) {
+               pthread_mutex_lock(&logev_lock);
+               pthread_cleanup_push(cleanup_mutex, &logev_lock);
+               while (!log_messages_pending)
+                       /* this is a cancellation point */
+                       pthread_cond_wait(&logev_cond, &logev_lock);
+               log_messages_pending = 0;
+               pthread_cleanup_pop(1);
+
+               flush_logqueue();
+       }
+       pthread_cleanup_pop(1);
+       return NULL;
+}
+
+void log_thread_start (pthread_attr_t *attr)
+{
+       int running = 0;
+
+       logdbg(stderr,"enter log_thread_start\n");
+
+       if (log_init("multipathd", 0)) {
+               fprintf(stderr,"can't initialize log buffer\n");
+               exit(1);
+       }
+
+       pthread_mutex_lock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       if (!pthread_create(&log_thr, attr, log_thread, NULL))
+               while (!(running = logq_running))
+                       pthread_cond_wait(&logev_cond, &logev_lock);
+       pthread_cleanup_pop(1);
+
+       if (!running) {
+               fprintf(stderr,"can't start log thread\n");
+               exit(1);
+       }
+
+       return;
+}
+
+void log_thread_reset (void)
+{
+       logdbg(stderr,"resetting log\n");
+       log_reset("multipathd");
+}
+
+void log_thread_stop (void)
+{
+       int running;
+
+       if (!la)
+               return;
+
+       logdbg(stderr,"enter log_thread_stop\n");
+
+       pthread_mutex_lock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       running = logq_running;
+       if (running) {
+               pthread_cancel(log_thr);
+               pthread_cond_signal(&logev_cond);
+       }
+       pthread_cleanup_pop(1);
+
+       if (running)
+               pthread_join(log_thr, NULL);
+
+       flush_logqueue();
+       log_close();
+}
diff --git a/libmpathutil/log_pthread.h b/libmpathutil/log_pthread.h
new file mode 100644 (file)
index 0000000..810ac92
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _LOG_PTHREAD_H
+#define _LOG_PTHREAD_H
+
+#include <pthread.h>
+
+void log_safe(int prio, const char * fmt, va_list ap)
+       __attribute__((format(printf, 2, 0)));
+void log_thread_start(pthread_attr_t *attr);
+void log_thread_reset (void);
+void log_thread_stop(void);
+
+#endif /* _LOG_PTHREAD_H */
diff --git a/libmpathutil/msort.c b/libmpathutil/msort.c
new file mode 100644 (file)
index 0000000..2963486
--- /dev/null
@@ -0,0 +1,271 @@
+/* An alternative to qsort, with an identical interface.
+   This file is part of the GNU C Library.
+   Copyright (C) 1992-2022 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "msort.h"
+
+typedef int(*__compar_d_fn_t)(const void *, const void *, void *);
+
+struct msort_param
+{
+  size_t s;
+  size_t var;
+  __compar_d_fn_t cmp;
+  void *arg;
+  char *t;
+};
+static void msort_with_tmp (const struct msort_param *p, void *b, size_t n);
+
+static void
+msort_with_tmp (const struct msort_param *p, void *b, size_t n)
+{
+  char *b1, *b2;
+  size_t n1, n2;
+
+  if (n <= 1)
+    return;
+
+  n1 = n / 2;
+  n2 = n - n1;
+  b1 = b;
+  b2 = (char *) b + (n1 * p->s);
+
+  msort_with_tmp (p, b1, n1);
+  msort_with_tmp (p, b2, n2);
+
+  char *tmp = p->t;
+  const size_t s = p->s;
+  __compar_d_fn_t cmp = p->cmp;
+  void *arg = p->arg;
+  switch (p->var)
+    {
+    case 0:
+      while (n1 > 0 && n2 > 0)
+       {
+         if ((*cmp) (b1, b2, arg) <= 0)
+           {
+             *(uint32_t *) tmp = *(uint32_t *) b1;
+             b1 += sizeof (uint32_t);
+             --n1;
+           }
+         else
+           {
+             *(uint32_t *) tmp = *(uint32_t *) b2;
+             b2 += sizeof (uint32_t);
+             --n2;
+           }
+         tmp += sizeof (uint32_t);
+       }
+      break;
+    case 1:
+      while (n1 > 0 && n2 > 0)
+       {
+         if ((*cmp) (b1, b2, arg) <= 0)
+           {
+             *(uint64_t *) tmp = *(uint64_t *) b1;
+             b1 += sizeof (uint64_t);
+             --n1;
+           }
+         else
+           {
+             *(uint64_t *) tmp = *(uint64_t *) b2;
+             b2 += sizeof (uint64_t);
+             --n2;
+           }
+         tmp += sizeof (uint64_t);
+       }
+      break;
+    case 2:
+      while (n1 > 0 && n2 > 0)
+       {
+         unsigned long *tmpl = (unsigned long *) tmp;
+         unsigned long *bl;
+
+         tmp += s;
+         if ((*cmp) (b1, b2, arg) <= 0)
+           {
+             bl = (unsigned long *) b1;
+             b1 += s;
+             --n1;
+           }
+         else
+           {
+             bl = (unsigned long *) b2;
+             b2 += s;
+             --n2;
+           }
+         while (tmpl < (unsigned long *) tmp)
+           *tmpl++ = *bl++;
+       }
+      break;
+    case 3:
+      while (n1 > 0 && n2 > 0)
+       {
+         if ((*cmp) (*(const void **) b1, *(const void **) b2, arg) <= 0)
+           {
+             *(void **) tmp = *(void **) b1;
+             b1 += sizeof (void *);
+             --n1;
+           }
+         else
+           {
+             *(void **) tmp = *(void **) b2;
+             b2 += sizeof (void *);
+             --n2;
+           }
+         tmp += sizeof (void *);
+       }
+      break;
+    default:
+      while (n1 > 0 && n2 > 0)
+       {
+         if ((*cmp) (b1, b2, arg) <= 0)
+           {
+             tmp = (char *) mempcpy (tmp, b1, s);
+             b1 += s;
+             --n1;
+           }
+         else
+           {
+             tmp = (char *) mempcpy (tmp, b2, s);
+             b2 += s;
+             --n2;
+           }
+       }
+      break;
+    }
+
+  if (n1 > 0)
+    memcpy (tmp, b1, n1 * s);
+  memcpy (b, p->t, (n - n2) * s);
+}
+
+
+static void
+msort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)
+{
+  size_t size = n * s;
+  char *tmp = NULL;
+  struct msort_param p;
+
+  /* For large object sizes use indirect sorting.  */
+  if (s > 32)
+    size = 2 * n * sizeof (void *) + s;
+
+  if (size < 1024)
+    /* The temporary array is small, so put it on the stack.  */
+    p.t = alloca (size);
+  else
+    {
+      /* It's somewhat large, so malloc it.  */
+      int save = errno;
+      tmp = malloc (size);
+      errno = save;
+      if (tmp == NULL)
+             return;
+      p.t = tmp;
+    }
+
+  p.s = s;
+  p.var = 4;
+  p.cmp = cmp;
+  p.arg = arg;
+
+  if (s > 32)
+    {
+      /* Indirect sorting.  */
+      char *ip = (char *) b;
+      void **tp = (void **) (p.t + n * sizeof (void *));
+      void **t = tp;
+      void *tmp_storage = (void *) (tp + n);
+
+      while ((void *) t < tmp_storage)
+       {
+         *t++ = ip;
+         ip += s;
+       }
+      p.s = sizeof (void *);
+      p.var = 3;
+      msort_with_tmp (&p, p.t + n * sizeof (void *), n);
+
+      /* tp[0] .. tp[n - 1] is now sorted, copy around entries of
+        the original array.  Knuth vol. 3 (2nd ed.) exercise 5.2-10.  */
+      char *kp;
+      size_t i;
+      for (i = 0, ip = (char *) b; i < n; i++, ip += s)
+       if ((kp = tp[i]) != ip)
+         {
+           size_t j = i;
+           char *jp = ip;
+           memcpy (tmp_storage, ip, s);
+
+           do
+             {
+               size_t k = (kp - (char *) b) / s;
+               tp[j] = jp;
+               memcpy (jp, kp, s);
+               j = k;
+               jp = kp;
+               kp = tp[k];
+             }
+           while (kp != ip);
+
+           tp[j] = jp;
+           memcpy (jp, tmp_storage, s);
+         }
+    }
+  else
+    {
+      if ((s & (sizeof (uint32_t) - 1)) == 0
+         && ((unsigned long) b) % __alignof__ (uint32_t) == 0)
+       {
+         if (s == sizeof (uint32_t))
+           p.var = 0;
+         else if (s == sizeof (uint64_t)
+                  && ((unsigned long) b) % __alignof__ (uint64_t) == 0)
+           p.var = 1;
+         else if ((s & (sizeof (unsigned long) - 1)) == 0
+                  && ((unsigned long) b)
+                     % __alignof__ (unsigned long) == 0)
+           p.var = 2;
+       }
+      msort_with_tmp (&p, b, n);
+    }
+  free (tmp);
+}
+
+/*
+ * glibc apparently doesn't use -Wcast-function-type.
+ * If this is safe for them, it should be for us, too.
+ */
+#pragma GCC diagnostic push
+#if __GNUC__ >= 8
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+void
+msort (void *b, size_t n, size_t s, __compar_fn_t cmp)
+{
+       return msort_r (b, n, s, (__compar_d_fn_t)cmp, NULL);
+}
+#pragma GCC diagnostic pop
diff --git a/libmpathutil/msort.h b/libmpathutil/msort.h
new file mode 100644 (file)
index 0000000..caef9b6
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __MSORT_H
+#define __MSORT_H
+typedef int(*__compar_fn_t)(const void *, const void *);
+void msort (void *b, size_t n, size_t s, __compar_fn_t cmp);
+
+#endif
diff --git a/libmpathutil/parser.c b/libmpathutil/parser.c
new file mode 100644 (file)
index 0000000..014d9b8
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * Part:        Configuration file parser/reader. Place into the dynamic
+ *              data structure representation the conf file
+ *
+ * Version:     $Id: parser.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
+ *
+ * Author:      Alexandre Cassen, <acassen@linux-vs.org>
+ *
+ *              This program is distributed in the hope that it will be useful,
+ *              but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *              See the GNU General Public License for more details.
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ */
+
+#include <syslog.h>
+#include <errno.h>
+
+#include "vector.h"
+#include "config.h"
+#include "parser.h"
+#include "debug.h"
+#include "strbuf.h"
+
+/* local vars */
+static int sublevel = 0;
+static int line_nr;
+
+int
+keyword_alloc(vector keywords, char *string,
+             handler_fn *handler,
+             print_fn *print,
+             int unique)
+{
+       struct keyword *keyword;
+
+       keyword = (struct keyword *)calloc(1, sizeof (struct keyword));
+
+       if (!keyword)
+               return 1;
+
+       if (!vector_alloc_slot(keywords)) {
+               free(keyword);
+               return 1;
+       }
+       keyword->string = string;
+       keyword->handler = handler;
+       keyword->print = print;
+       keyword->unique = unique;
+
+       vector_set_slot(keywords, keyword);
+
+       return 0;
+}
+
+void
+install_sublevel(void)
+{
+       sublevel++;
+}
+
+void
+install_sublevel_end(void)
+{
+       sublevel--;
+}
+
+int
+_install_keyword(vector keywords, char *string,
+                handler_fn *handler,
+                print_fn *print,
+                int unique)
+{
+       int i = 0;
+       struct keyword *keyword;
+
+       /* fetch last keyword */
+       keyword = VECTOR_LAST_SLOT(keywords);
+       if (!keyword)
+               return 1;
+
+       /* position to last sub level */
+       for (i = 0; i < sublevel; i++) {
+               keyword = VECTOR_LAST_SLOT(keyword->sub);
+               if (!keyword)
+                       return 1;
+       }
+
+       /* First sub level allocation */
+       if (!keyword->sub)
+               keyword->sub = vector_alloc();
+
+       if (!keyword->sub)
+               return 1;
+
+       /* add new sub keyword */
+       return keyword_alloc(keyword->sub, string, handler, print, unique);
+}
+
+void
+free_keywords(vector keywords)
+{
+       struct keyword *keyword;
+       int i;
+
+       if (!keywords)
+               return;
+
+       for (i = 0; i < VECTOR_SIZE(keywords); i++) {
+               keyword = VECTOR_SLOT(keywords, i);
+               if (keyword->sub)
+                       free_keywords(keyword->sub);
+               free(keyword);
+       }
+       vector_free(keywords);
+}
+
+struct keyword *
+find_keyword(vector keywords, vector v, char * name)
+{
+       struct keyword *keyword;
+       int i;
+       size_t len;
+
+       if (!name || !keywords)
+               return NULL;
+
+       if (!v)
+               v = keywords;
+
+       len = strlen(name);
+
+       for (i = 0; i < VECTOR_SIZE(v); i++) {
+               keyword = VECTOR_SLOT(v, i);
+               if ((strlen(keyword->string) == len) &&
+                   !strcmp(keyword->string, name))
+                       return keyword;
+               if (keyword->sub) {
+                       keyword = find_keyword(keywords, keyword->sub, name);
+                       if (keyword)
+                               return keyword;
+               }
+       }
+       return NULL;
+}
+
+int
+snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
+               const void *data)
+{
+       int r;
+       char *f;
+       struct config *conf;
+       STRBUF_ON_STACK(sbuf);
+
+       if (!kw || !kw->print)
+               return 0;
+
+       do {
+               f = strchr(fmt, '%');
+               if (f == NULL) {
+                       r = append_strbuf_str(&sbuf, fmt);
+                       goto out;
+               }
+               if (f != fmt &&
+                   (r = __append_strbuf_str(&sbuf, fmt, f - fmt)) < 0)
+                       goto out;
+               fmt = f + 1;
+               switch(*fmt) {
+               case 'k':
+                       if ((r = append_strbuf_str(&sbuf, kw->string)) < 0)
+                               goto out;
+                       break;
+               case 'v':
+                       conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       r = kw->print(conf, &sbuf, data);
+                       pthread_cleanup_pop(1);
+                       if (r < 0)
+                               goto out;
+                       else if (r == 0) {/* no output if no value */
+                               reset_strbuf(&sbuf);
+                               goto out;
+                       }
+                       break;
+               }
+       } while (*fmt++);
+out:
+       return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
+                                  get_strbuf_len(&sbuf));
+}
+
+static const char quote_marker[] = { '\0', '"', '\0' };
+bool is_quote(const char* token)
+{
+       return token[0] == quote_marker[0] &&
+               token[1] == quote_marker[1] &&
+               token[2] == quote_marker[2];
+}
+
+vector
+alloc_strvec(char *string)
+{
+       char *cp, *start, *token;
+       int strlen;
+       int in_string;
+       vector strvec;
+
+       if (!string)
+               return NULL;
+
+       cp = string;
+
+       /* Skip white spaces */
+       while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0')
+               cp++;
+
+       /* Return if there is only white spaces */
+       if (*cp == '\0')
+               return NULL;
+
+       /* Return if string begin with a comment */
+       if (*cp == '!' || *cp == '#')
+               return NULL;
+
+       /* Create a vector and alloc each command piece */
+       strvec = vector_alloc();
+
+       if (!strvec)
+               return NULL;
+
+       in_string = 0;
+       while (1) {
+               int two_quotes = 0;
+
+               if (!vector_alloc_slot(strvec))
+                       goto out;
+
+               vector_set_slot(strvec, NULL);
+               start = cp;
+               if (*cp == '"' && !(in_string && *(cp + 1) == '"')) {
+                       cp++;
+                       token = calloc(1, sizeof(quote_marker));
+
+                       if (!token)
+                               goto out;
+
+                       memcpy(token, quote_marker, sizeof(quote_marker));
+                       if (in_string)
+                               in_string = 0;
+                       else
+                               in_string = 1;
+               } else if (!in_string && (*cp == '{' || *cp == '}')) {
+                       token = malloc(2);
+
+                       if (!token)
+                               goto out;
+
+                       *(token) = *cp;
+                       *(token + 1) = '\0';
+                       cp++;
+               } else {
+
+               move_on:
+                       while ((in_string ||
+                               (!isspace((int) *cp) && isascii((int) *cp) &&
+                                *cp != '!' && *cp != '#' && *cp != '{' &&
+                                *cp != '}')) && *cp != '\0' && *cp != '"')
+                               cp++;
+
+                       /* Two consecutive double quotes - don't end string */
+                       if (in_string && *cp == '"') {
+                               if (*(cp + 1) == '"') {
+                                       two_quotes = 1;
+                                       cp += 2;
+                                       goto move_on;
+                               }
+                       }
+
+                       strlen = cp - start;
+                       token = calloc(1, strlen + 1);
+
+                       if (!token)
+                               goto out;
+
+                       memcpy(token, start, strlen);
+                       *(token + strlen) = '\0';
+
+                       /* Replace "" by " */
+                       if (two_quotes) {
+                               char *qq = strstr(token, "\"\"");
+                               while (qq != NULL) {
+                                       memmove(qq + 1, qq + 2,
+                                               strlen + 1 - (qq + 2 - token));
+                                       qq = strstr(qq + 1, "\"\"");
+                               }
+                       }
+               }
+               vector_set_slot(strvec, token);
+
+               while ((!in_string &&
+                       (isspace((int) *cp) || !isascii((int) *cp)))
+                      && *cp != '\0')
+                       cp++;
+               if (*cp == '\0' ||
+                   (!in_string && (*cp == '!' || *cp == '#'))) {
+                       return strvec;
+               }
+       }
+out:
+       vector_free(strvec);
+       return NULL;
+}
+
+static int
+read_line(FILE *stream, char *buf, int size)
+{
+       char *p;
+
+       if (fgets(buf, size, stream) == NULL)
+               return 0;
+       strtok_r(buf, "\n\r", &p);
+       return 1;
+}
+
+void *
+set_value(vector strvec)
+{
+       char *str = VECTOR_SLOT(strvec, 1);
+       size_t size;
+       int i = 0;
+       int len = 0;
+       char *alloc = NULL;
+       char *tmp;
+
+       if (!str) {
+               condlog(0, "option '%s' missing value",
+                       (char *)VECTOR_SLOT(strvec, 0));
+               return NULL;
+       }
+       if (!is_quote(str)) {
+               size = strlen(str);
+               if (size == 0) {
+                       condlog(0, "option '%s' has empty value",
+                               (char *)VECTOR_SLOT(strvec, 0));
+                       return NULL;
+               }
+               alloc = calloc(1, sizeof (char) * (size + 1));
+               if (alloc)
+                       memcpy(alloc, str, size);
+               else
+                       goto oom;
+               return alloc;
+       }
+       /* Even empty quotes counts as a value (An empty string) */
+       alloc = (char *)calloc(1, sizeof (char));
+       if (!alloc)
+               goto oom;
+       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+               str = VECTOR_SLOT(strvec, i);
+               if (!str) {
+                       free(alloc);
+                       condlog(0, "parse error for option '%s'",
+                               (char *)VECTOR_SLOT(strvec, 0));
+                       return NULL;
+               }
+               if (is_quote(str))
+                       break;
+               tmp = alloc;
+               /* The first +1 is for the NULL byte. The rest are for the
+                * spaces between words */
+               len += strlen(str) + 1;
+               alloc = realloc(alloc, sizeof (char) * len);
+               if (!alloc) {
+                       free(tmp);
+                       goto oom;
+               }
+               if (*alloc != '\0')
+                       strncat(alloc, " ", len - strlen(alloc));
+               strncat(alloc, str, len - strlen(alloc) - 1);
+       }
+       return alloc;
+oom:
+       condlog(0, "can't allocate memory for option '%s'",
+               (char *)VECTOR_SLOT(strvec, 0));
+       return NULL;
+}
+
+/* non-recursive configuration stream handler */
+static int kw_level = 0;
+
+int warn_on_duplicates(vector uniques, char *str, const char *file)
+{
+       char *tmp;
+       int i;
+
+       vector_foreach_slot(uniques, tmp, i) {
+               if (!strcmp(str, tmp)) {
+                       condlog(1, "%s line %d, duplicate keyword: %s",
+                               file, line_nr, str);
+                       return 0;
+               }
+       }
+       tmp = strdup(str);
+       if (!tmp)
+               return 1;
+       if (!vector_alloc_slot(uniques)) {
+               free(tmp);
+               return 1;
+       }
+       vector_set_slot(uniques, tmp);
+       return 0;
+}
+
+void free_uniques(vector uniques)
+{
+       char *tmp;
+       int i;
+
+       vector_foreach_slot(uniques, tmp, i)
+               free(tmp);
+       vector_free(uniques);
+}
+
+int
+is_sublevel_keyword(char *str)
+{
+       return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 ||
+               strcmp(str, "blacklist_exceptions") == 0 ||
+               strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 ||
+               strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 ||
+               strcmp(str, "multipath") == 0);
+}
+
+int
+validate_config_strvec(vector strvec, const char *file)
+{
+       char *str = NULL;
+       int i;
+
+       if (strvec && VECTOR_SIZE(strvec) > 0)
+               str = VECTOR_SLOT(strvec, 0);
+
+       if (str == NULL) {
+               condlog(0, "can't parse option on line %d of %s",
+                       line_nr, file);
+               return -1;
+       }
+       if (*str == '}') {
+               if (VECTOR_SIZE(strvec) > 1)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file);
+               return 0;
+       }
+       if (*str == '{') {
+               condlog(0, "invalid keyword '%s' on line %d of %s",
+                       str, line_nr, file);
+               return -1;
+       }
+       if (is_sublevel_keyword(str)) {
+               str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
+               if (str == NULL)
+                       condlog(0, "missing '{' on line %d of %s",
+                               line_nr, file);
+               else if (*str != '{')
+                       condlog(0, "expecting '{' on line %d of %s. found '%s'",
+                               line_nr, file, str);
+               else if (VECTOR_SIZE(strvec) > 2)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+               return 0;
+       }
+       str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
+       if (str == NULL) {
+               condlog(0, "missing value for option '%s' on line %d of %s",
+                       (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
+               return -1;
+       }
+       if (!is_quote(str)) {
+               if (VECTOR_SIZE(strvec) > 2)
+                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+               return 0;
+       }
+       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+               str = VECTOR_SLOT(strvec, i);
+               if (str == NULL) {
+                       condlog(0, "can't parse value on line %d of %s",
+                               line_nr, file);
+                       return -1;
+               }
+               if (is_quote(str)) {
+                       if (VECTOR_SIZE(strvec) > i + 1)
+                               condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file);
+                       return 0;
+               }
+       }
+       condlog(0, "missing closing quotes on line %d of %s",
+               line_nr, file);
+       return 0;
+}
+
+static int
+process_stream(struct config *conf, FILE *stream, vector keywords,
+              const char *section, const char *file)
+{
+       int i;
+       int r = 0, t;
+       struct keyword *keyword;
+       char *str;
+       char *buf;
+       vector strvec;
+       vector uniques;
+
+       uniques = vector_alloc();
+       if (!uniques)
+               return 1;
+
+       buf = calloc(1, MAXBUF);
+
+       if (!buf) {
+               vector_free(uniques);
+               return 1;
+       }
+
+       while (read_line(stream, buf, MAXBUF)) {
+               line_nr++;
+               strvec = alloc_strvec(buf);
+               if (!strvec)
+                       continue;
+
+               if (validate_config_strvec(strvec, file) != 0) {
+                       free_strvec(strvec);
+                       continue;
+               }
+
+               str = VECTOR_SLOT(strvec, 0);
+
+               if (!strcmp(str, EOB)) {
+                       if (kw_level > 0) {
+                               free_strvec(strvec);
+                               goto out;
+                       }
+                       condlog(0, "unmatched '%s' at line %d of %s",
+                               EOB, line_nr, file);
+               }
+
+               for (i = 0; i < VECTOR_SIZE(keywords); i++) {
+                       keyword = VECTOR_SLOT(keywords, i);
+
+                       if (!strcmp(keyword->string, str)) {
+                               if (keyword->unique &&
+                                   warn_on_duplicates(uniques, str, file)) {
+                                               r = 1;
+                                               free_strvec(strvec);
+                                               goto out;
+                               }
+                               if (keyword->handler) {
+                                   t = keyword->handler(conf, strvec, file,
+                                                        line_nr);
+                                       r += t;
+                                       if (t)
+                                               condlog(1, "%s line %d, parsing failed: %s",
+                                                       file, line_nr, buf);
+                               }
+
+                               if (keyword->sub) {
+                                       kw_level++;
+                                       r += process_stream(conf, stream,
+                                                           keyword->sub,
+                                                           keyword->string,
+                                                           file);
+                                       kw_level--;
+                               }
+                               break;
+                       }
+               }
+               if (i >= VECTOR_SIZE(keywords)) {
+                       if (section)
+                               condlog(1, "%s line %d, invalid keyword in the %s section: %s",
+                                       file, line_nr, section, str);
+                       else
+                               condlog(1, "%s line %d, invalid keyword: %s",
+                                       file, line_nr, str);
+               }
+               free_strvec(strvec);
+       }
+       if (kw_level == 1)
+               condlog(1, "missing '%s' at end of %s", EOB, file);
+out:
+       free(buf);
+       free_uniques(uniques);
+       return r;
+}
+
+/* Data initialization */
+int
+process_file(struct config *conf, const char *file)
+{
+       int r;
+       FILE *stream;
+
+       if (!conf->keywords) {
+               condlog(0, "No keywords allocated");
+               return 1;
+       }
+       stream = fopen(file, "r");
+       if (!stream) {
+               condlog(0, "couldn't open configuration file '%s': %s",
+                       file, strerror(errno));
+               return 1;
+       }
+
+       /* Stream handling */
+       line_nr = 0;
+       r = process_stream(conf, stream, conf->keywords, NULL, file);
+       fclose(stream);
+       //free_keywords(keywords);
+
+       return r;
+}
diff --git a/libmpathutil/parser.h b/libmpathutil/parser.h
new file mode 100644 (file)
index 0000000..9d9d948
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Soft:        Keepalived is a failover program for the LVS project
+ *              <www.linuxvirtualserver.org>. It monitor & manipulate
+ *              a loadbalanced server pool using multi-layer checks.
+ *
+ * Part:        cfreader.c include file.
+ *
+ * Version:     $Id: parser.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
+ *
+ * Author:      Alexandre Cassen, <acassen@linux-vs.org>
+ *
+ *              This program is distributed in the hope that it will be useful,
+ *              but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *              See the GNU General Public License for more details.
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _PARSER_H
+#define _PARSER_H
+
+/* system includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <ctype.h>
+
+/* local includes */
+#include "vector.h"
+struct strbuf;
+struct config;
+
+/* Global definitions */
+#define EOB  "}"
+#define MAXBUF 1024
+
+
+/* keyword definition */
+typedef int print_fn(struct config *, struct strbuf *, const void *);
+typedef int handler_fn(struct config *, vector, const char *file, int line_nr);
+
+struct keyword {
+       char *string;
+       handler_fn *handler;
+       print_fn *print;
+       vector sub;
+       int unique;
+};
+
+/* Reloading helpers */
+#define SET_RELOAD      (reload = 1)
+#define UNSET_RELOAD    (reload = 0)
+#define RELOAD_DELAY    5
+
+/* iterator helper */
+#define iterate_sub_keywords(k,p,i) \
+       for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++)
+
+/* Prototypes */
+extern int keyword_alloc(vector keywords, char *string, handler_fn *handler,
+                        print_fn *print, int unique);
+#define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1)
+extern void install_sublevel(void);
+extern void install_sublevel_end(void);
+
+extern int _install_keyword(vector keywords, char *string, handler_fn *handler,
+                           print_fn *print, int unique);
+#define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1)
+#define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0)
+extern void dump_keywords(vector keydump, int level);
+extern void free_keywords(vector keywords);
+extern vector alloc_strvec(char *string);
+extern void *set_value(vector strvec);
+extern int process_file(struct config *conf, const char *conf_file);
+extern struct keyword * find_keyword(vector keywords, vector v, char * name);
+int snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
+                   const void *data);
+bool is_quote(const char* token);
+
+#endif
diff --git a/libmpathutil/strbuf.c b/libmpathutil/strbuf.c
new file mode 100644 (file)
index 0000000..e23b65e
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <assert.h>
+#include "strbuf.h"
+
+static const char empty_str[] = "";
+
+char *__get_strbuf_buf(struct strbuf *buf)
+{
+       return buf->buf;
+}
+
+const char *get_strbuf_str(const struct strbuf *buf)
+{
+       return buf->buf ? buf->buf : empty_str;
+}
+
+char *steal_strbuf_str(struct strbuf *buf)
+{
+       char *p = buf->buf;
+
+       buf->buf = NULL;
+       buf->size = buf->offs = 0;
+       return p;
+}
+
+size_t get_strbuf_len(const struct strbuf *buf)
+{
+       return buf->offs;
+}
+
+static bool strbuf_is_sane(const struct strbuf *buf)
+{
+       return buf && ((!buf->buf && !buf->size && !buf->offs) ||
+                      (buf->buf && buf->size && buf->size > buf->offs));
+}
+
+void reset_strbuf(struct strbuf *buf)
+{
+       free(buf->buf);
+       buf->buf = NULL;
+       buf->size = buf->offs = 0;
+}
+
+void free_strbuf(struct strbuf *buf)
+{
+       if (!buf)
+               return;
+       reset_strbuf(buf);
+       free(buf);
+}
+
+struct strbuf *new_strbuf(void)
+{
+       return calloc(1, sizeof(struct strbuf));
+}
+
+int truncate_strbuf(struct strbuf *buf, size_t offs)
+{
+       if (!buf->buf)
+               return -EFAULT;
+       if (offs > buf->offs)
+               return -ERANGE;
+
+       buf->offs = offs;
+       buf->buf[offs] = '\0';
+       return 0;
+}
+
+#define BUF_CHUNK 64
+
+static int expand_strbuf(struct strbuf *buf, int addsz)
+{
+       size_t add;
+       char *tmp;
+
+       assert(strbuf_is_sane(buf));
+       if (addsz < 0)
+               return -EINVAL;
+       if (buf->size - buf->offs >= (size_t)addsz + 1)
+               return 0;
+
+       add = ((addsz - (buf->size - buf->offs)) / BUF_CHUNK + 1)
+               * BUF_CHUNK;
+
+       if (buf->size >= SIZE_MAX - add) {
+               add = SIZE_MAX - buf->size;
+               if (add < (size_t)addsz + 1)
+                       return -EOVERFLOW;
+       }
+
+       tmp = realloc(buf->buf, buf->size + add);
+       if (!tmp)
+               return -ENOMEM;
+
+       buf->buf = tmp;
+       buf->size += add;
+       buf->buf[buf->offs] = '\0';
+
+       return 0;
+}
+
+int __append_strbuf_str(struct strbuf *buf, const char *str, int slen)
+{
+       int ret;
+
+       if ((ret = expand_strbuf(buf, slen)) < 0)
+               return ret;
+
+       memcpy(buf->buf + buf->offs, str, slen);
+       buf->offs += slen;
+       buf->buf[buf->offs] = '\0';
+
+       return slen;
+}
+
+int append_strbuf_str(struct strbuf *buf, const char *str)
+{
+       size_t slen;
+
+       if (!str)
+               return -EINVAL;
+
+       slen = strlen(str);
+       if (slen > INT_MAX)
+               return -ERANGE;
+
+       return __append_strbuf_str(buf, str, slen);
+}
+
+int fill_strbuf(struct strbuf *buf, char c, int slen)
+{
+       int ret;
+
+       if ((ret = expand_strbuf(buf, slen)) < 0)
+               return ret;
+
+       memset(buf->buf + buf->offs, c, slen);
+       buf->offs += slen;
+       buf->buf[buf->offs] = '\0';
+
+       return slen;
+}
+
+int append_strbuf_quoted(struct strbuf *buff, const char *ptr)
+{
+       char *quoted, *q;
+       const char *p;
+       unsigned n_quotes, i;
+       size_t qlen;
+       int ret;
+
+       if (!ptr)
+               return -EINVAL;
+
+       for (n_quotes = 0, p = strchr(ptr, '"'); p; p = strchr(++p, '"'))
+               n_quotes++;
+
+       /* leading + trailing quote, 1 extra quote for every quote in ptr */
+       qlen = strlen(ptr) + 2 + n_quotes;
+       if (qlen > INT_MAX)
+               return -ERANGE;
+       if ((ret = expand_strbuf(buff, qlen)) < 0)
+               return ret;
+
+       quoted = &(buff->buf[buff->offs]);
+       *quoted++ = '"';
+       for (p = ptr, q = quoted, i = 0; i < n_quotes; i++) {
+               char *q1 = memccpy(q, p, '"', qlen - 2 - (q - quoted));
+
+               assert(q1 != NULL);
+               p += q1 - q;
+               *q1++ = '"';
+               q = q1;
+       }
+       q = mempcpy(q, p, qlen - 2 - (q - quoted));
+       *q++ = '"';
+       *q = '\0';
+       ret = q - &(buff->buf[buff->offs]);
+       buff->offs += ret;
+       return ret;
+}
+
+__attribute__((format(printf, 2, 3)))
+int print_strbuf(struct strbuf *buf, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+       char *tail;
+
+       va_start(ap, fmt);
+       ret = vasprintf(&tail, fmt, ap);
+       va_end(ap);
+
+       if (ret < 0)
+               return -ENOMEM;
+
+       ret = __append_strbuf_str(buf, tail, ret);
+
+       free(tail);
+       return ret;
+}
diff --git a/libmpathutil/strbuf.h b/libmpathutil/strbuf.h
new file mode 100644 (file)
index 0000000..ae86341
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef _STRBUF_H
+#define _STRBUF_H
+#include <errno.h>
+#include <string.h>
+
+struct strbuf {
+       char *buf;
+       size_t size;
+       size_t offs;
+};
+
+/**
+ * reset_strbuf(): prepare strbuf for new content
+ * @param strbuf: string buffer to reset
+ *
+ * Frees internal buffer and resets size and offset to 0.
+ * Can be used to cleanup a struct strbuf on stack.
+ */
+void reset_strbuf(struct strbuf *buf);
+
+/**
+ * free_strbuf(): free resources
+ * @param strbuf: string buffer to discard
+ *
+ * Frees all memory occupied by a struct strbuf.
+ */
+void free_strbuf(struct strbuf *buf);
+
+/**
+ * macro: STRBUF_INIT
+ *
+ * Use this to initialize a local struct strbuf on the stack,
+ * or in a global/static variable.
+ */
+#define STRBUF_INIT { .buf = NULL, }
+
+/**
+ * macro: STRBUF_ON_STACK
+ *
+ * Define and initialize a local struct @strbuf to be cleaned up when
+ * the current scope is left
+ */
+#define STRBUF_ON_STACK(__x)                                           \
+       struct strbuf __attribute__((cleanup(reset_strbuf))) (__x) = STRBUF_INIT;
+
+/**
+ * new_strbuf(): allocate a struct strbuf on the heap
+ *
+ * @returns: pointer to allocated struct, or NULL in case of error.
+ */
+struct strbuf *new_strbuf(void);
+
+/**
+ * get_strbuf_buf(): retrieve a pointer to the strbuf's buffer
+ * @param buf: a struct strbuf
+ * @returns: pointer to the string written to the strbuf so far.
+ *
+ * INTERNAL ONLY.
+ * DANGEROUS: Unlike the return value of get_strbuf_str(),
+ * this string can be written to, modifying the strbuf's content.
+ * USE WITH CAUTION.
+ * If @strbuf was never written to, the function returns NULL.
+ * The return value of this function must not be free()d.
+ */
+char *__get_strbuf_buf(struct strbuf *buf);
+
+/**
+ * get_strbuf_str(): retrieve string from strbuf
+ * @param buf: a struct strbuf
+ * @returns: pointer to the string written to the strbuf so far.
+ *
+ * If @strbuf was never written to, the function returns a zero-
+ * length string. The return value of this function must not be
+ * free()d.
+ */
+const char *get_strbuf_str(const struct strbuf *buf);
+
+/**
+ * steal_strbuf_str(): retrieve string from strbuf and reset
+ * @param buf: a struct strbuf
+ * @returns: pointer to the string written to @strbuf, or NULL
+ *
+ * After calling this function, the @strbuf is empty as if freshly
+ * initialized. The caller is responsible to free() the returned pointer.
+ * If @strbuf was never written to (not even an empty string was appended),
+ * the function returns NULL.
+ */
+char *steal_strbuf_str(struct strbuf *buf);
+
+/**
+ * get_strbuf_len(): retrieve string length from strbuf
+ * @param buf: a struct strbuf
+ * @returns: the length of the string written to @strbuf so far.
+ */
+size_t get_strbuf_len(const struct strbuf *buf);
+
+/**
+ * truncate_strbuf(): shorten the buffer
+ * @param buf: struct strbuf to truncate
+ * @param offs: new buffer position / offset
+ * @returns: 0 on success, negative error code otherwise.
+ *
+ * If @strbuf is freshly allocated/reset (never written to), -EFAULT
+ * is returned. if @offs must be higher than the current offset as returned
+ * by get_strbuf_len(), -ERANGE is returned. The allocated size of the @strbuf
+ * remains unchanged.
+ */
+int truncate_strbuf(struct strbuf *buf, size_t offs);
+
+/**
+ * __append_strbuf_str(): append string of known length
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, not necessarily 0-terminated
+ * @param slen: max number of characters to append, must be non-negative
+ * @returns: @slen = number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise.
+ *
+ * Notes: a 0-byte is always appended to the output buffer after @slen characters.
+ * 0-bytes possibly contained in the first @slen characters are copied into
+ * the output. If the function returns an error, @strbuf is unchanged.
+ */
+int __append_strbuf_str(struct strbuf *buf, const char *str, int slen);
+
+/**
+ * append_strbuf_str(): append string
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, 0-terminated
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given 0-terminated string to @strbuf, expanding @strbuf's size
+ * as necessary. If the function returns an error, @strbuf is unchanged.
+ */
+int append_strbuf_str(struct strbuf *buf, const char *str);
+
+/**
+ * fill_strbuf_str(): pad strbuf with a character
+ * @param buf: the struct strbuf to write to
+ * @param c: the character used for filling
+ * @param slen: max number of characters to append, must be non-negative
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given character @slen times to @strbuf, expanding @strbuf's size
+ * as necessary. If the function returns an error, @strbuf is unchanged.
+ */
+int fill_strbuf(struct strbuf *buf, char c, int slen);
+
+/**
+ * append_strbuf_quoted(): append string in double quotes, escaping quotes in string
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, 0-terminated
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given string to @strbuf, with leading and trailing double
+ * quotes (") added, expanding @strbuf's size as necessary. Any double quote
+ * characters (") in the string are transformed to a pair of double quotes ("").
+ * If the function returns an error, @strbuf is unchanged.
+ */
+int append_strbuf_quoted(struct strbuf *buf, const char *str);
+
+/**
+ * print_strbuf(): print to strbuf, formatted
+ * @param buf: the struct strbuf to print to
+ * @param fmt: printf()-like format string
+ * @returns: number of appended characters if successful, (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the arguments following @fmt, formatted as in printf(), to
+ * @strbuf, expanding @strbuf's size as necessary. The function makes sure that
+ * the output @strbuf is always 0-terminated.
+ * If the function returns an error, @strbuf is unchanged.
+ */
+__attribute__((format(printf, 2, 3)))
+int print_strbuf(struct strbuf *buf, const char *fmt, ...);
+
+#endif
diff --git a/libmpathutil/time-util.c b/libmpathutil/time-util.c
new file mode 100644 (file)
index 0000000..2919300
--- /dev/null
@@ -0,0 +1,63 @@
+#include <assert.h>
+#include <pthread.h>
+#include <time.h>
+#include "time-util.h"
+
+void get_monotonic_time(struct timespec *res)
+{
+       struct timespec ts;
+       int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+       assert(rv == 0);
+       *res = ts;
+}
+
+/* Initialize @cond as a condition variable that uses the monotonic clock */
+void pthread_cond_init_mono(pthread_cond_t *cond)
+{
+       pthread_condattr_t attr;
+       int res;
+
+       res = pthread_condattr_init(&attr);
+       assert(res == 0);
+       res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+       assert(res == 0);
+       res = pthread_cond_init(cond, &attr);
+       assert(res == 0);
+       res = pthread_condattr_destroy(&attr);
+       assert(res == 0);
+}
+
+/* Ensure that 0 <= ts->tv_nsec && ts->tv_nsec < 1000 * 1000 * 1000. */
+void normalize_timespec(struct timespec *ts)
+{
+       while (ts->tv_nsec < 0) {
+               ts->tv_nsec += 1000L * 1000 * 1000;
+               ts->tv_sec--;
+       }
+       while (ts->tv_nsec >= 1000L * 1000 * 1000) {
+               ts->tv_nsec -= 1000L * 1000 * 1000;
+               ts->tv_sec++;
+       }
+}
+
+/* Compute *res = *a - *b */
+void timespecsub(const struct timespec *a, const struct timespec *b,
+                struct timespec *res)
+{
+       res->tv_sec = a->tv_sec - b->tv_sec;
+       res->tv_nsec = a->tv_nsec - b->tv_nsec;
+       normalize_timespec(res);
+}
+
+int timespeccmp(const struct timespec *a, const struct timespec *b)
+{
+       struct timespec tmp;
+
+       timespecsub(a, b, &tmp);
+       if (tmp.tv_sec > 0)
+               return 1;
+       if (tmp.tv_sec < 0)
+               return -1;
+       return tmp.tv_nsec > 0 ? 1 : (tmp.tv_nsec < 0 ? -1 : 0);
+}
diff --git a/libmpathutil/time-util.h b/libmpathutil/time-util.h
new file mode 100644 (file)
index 0000000..4a80ebd
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _TIME_UTIL_H_
+#define _TIME_UTIL_H_
+
+#include <pthread.h>
+
+struct timespec;
+
+void get_monotonic_time(struct timespec *res);
+void pthread_cond_init_mono(pthread_cond_t *cond);
+void normalize_timespec(struct timespec *ts);
+void timespecsub(const struct timespec *a, const struct timespec *b,
+                struct timespec *res);
+int timespeccmp(const struct timespec *a, const struct timespec *b);
+
+#endif /* _TIME_UTIL_H_ */
diff --git a/libmpathutil/util.c b/libmpathutil/util.c
new file mode 100644 (file)
index 0000000..6692ac2
--- /dev/null
@@ -0,0 +1,450 @@
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libudev.h>
+
+#include "util.h"
+#include "debug.h"
+#include "checkers.h"
+#include "vector.h"
+#include "structs.h"
+#include "config.h"
+#include "log.h"
+
+size_t
+strchop(char *str)
+{
+       size_t i;
+
+       for (i = strlen(str) - 1; i != (size_t) -1 && isspace(str[i]); i--) ;
+       str[++i] = '\0';
+       return i;
+}
+
+#ifndef __GLIBC__
+/*
+ * glibc's non-destructive version of basename()
+ * License: LGPL-2.1-or-later
+ */
+static const char *__basename(const char *filename)
+{
+       char *p = strrchr(filename, '/');
+       return p ? p + 1 : filename;
+}
+#define basename(x) __basename(x)
+#endif
+
+int
+basenamecpy (const char *src, char *dst, size_t size)
+{
+       const char *p, *e;
+
+       if (!src || !dst || !strlen(src))
+               return 0;
+
+       p = basename(src);
+
+       for (e = p + strlen(p) - 1; e >= p && isspace(*e); --e) ;
+       if (e < p || (size_t)(e - p) > size - 2)
+               return 0;
+
+       strlcpy(dst, p, e - p + 2);
+       return strlen(dst);
+}
+
+int
+filepresent (const char *run) {
+       struct stat buf;
+
+       if(!stat(run, &buf))
+               return 1;
+       return 0;
+}
+
+char *get_next_string(char **temp, const char *split_char)
+{
+       char *token = NULL;
+       token = strsep(temp, split_char);
+       while (token != NULL && !strcmp(token, ""))
+               token = strsep(temp, split_char);
+       return token;
+}
+
+int
+get_word (const char *sentence, char **word)
+{
+       const char *p;
+       int len;
+       int skip = 0;
+
+       if (word)
+               *word = NULL;
+
+       while (*sentence ==  ' ') {
+               sentence++;
+               skip++;
+       }
+       if (*sentence == '\0')
+               return 0;
+
+       p = sentence;
+
+       while (*p !=  ' ' && *p != '\0')
+               p++;
+
+       len = (int) (p - sentence);
+
+       if (!word)
+               return skip + len;
+
+       *word = calloc(1, len + 1);
+
+       if (!*word) {
+               condlog(0, "get_word : oom");
+               return 0;
+       }
+       strncpy(*word, sentence, len);
+       strchop(*word);
+       condlog(5, "*word = %s, len = %i", *word, len);
+
+       if (*p == '\0')
+               return 0;
+
+       return skip + len;
+}
+
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size)
+{
+       size_t bytes = 0;
+       char ch;
+
+       while ((ch = *src++)) {
+               if (bytes + 1 < size)
+                       *dst++ = ch;
+               bytes++;
+       }
+
+       /* If size == 0 there is no space for a final null... */
+       if (size)
+               *dst = '\0';
+       return bytes;
+}
+
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size)
+{
+       size_t bytes = 0;
+       char ch;
+
+       while (bytes < size && *dst) {
+               dst++;
+               bytes++;
+       }
+       if (bytes == size)
+               return (bytes + strlen(src));
+
+       while ((ch = *src++)) {
+               if (bytes + 1 < size)
+                       *dst++ = ch;
+               bytes++;
+       }
+
+       *dst = '\0';
+       return bytes;
+}
+
+int devt2devname(char *devname, int devname_len, const char *devt)
+{
+       struct udev_device *u_dev;
+       const char * dev_name;
+       int r;
+
+       if (!devname || !devname_len || !devt)
+               return 1;
+
+       u_dev = udev_device_new_from_devnum(udev, 'b', parse_devt(devt));
+       if (!u_dev) {
+               condlog(0, "\"%s\": invalid major/minor numbers, not found in sysfs", devt);
+               return 1;
+       }
+
+       dev_name = udev_device_get_sysname(u_dev);
+       if (!dev_name) {
+               udev_device_unref(u_dev);
+               return 1;
+       }
+       r = strlcpy(devname, dev_name, devname_len);
+       udev_device_unref(u_dev);
+
+       return !(r < devname_len);
+}
+
+/* This function returns a pointer inside of the supplied pathname string.
+ * If is_path_device is true, it may also modify the supplied string */
+char *convert_dev(char *name, int is_path_device)
+{
+       char *ptr;
+
+       if (!name)
+               return NULL;
+       if (is_path_device) {
+               ptr = strstr(name, "cciss/");
+               if (ptr) {
+                       ptr += 5;
+                       *ptr = '!';
+               }
+       }
+       if (!strncmp(name, "/dev/", 5) && strlen(name) > 5)
+               ptr = name + 5;
+       else
+               ptr = name;
+       return ptr;
+}
+
+dev_t parse_devt(const char *dev_t)
+{
+       int maj, min;
+
+       if (sscanf(dev_t,"%d:%d", &maj, &min) != 2)
+               return 0;
+
+       return makedev(maj, min);
+}
+
+void
+setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
+{
+       int ret;
+
+       ret = pthread_attr_init(attr);
+       assert(ret == 0);
+       if (PTHREAD_STACK_MIN > 0 && stacksize < (size_t)PTHREAD_STACK_MIN)
+               stacksize = (size_t)PTHREAD_STACK_MIN;
+       ret = pthread_attr_setstacksize(attr, stacksize);
+       assert(ret == 0);
+       if (detached) {
+               ret = pthread_attr_setdetachstate(attr,
+                                                 PTHREAD_CREATE_DETACHED);
+               assert(ret == 0);
+       }
+}
+
+int systemd_service_enabled_in(const char *dev, const char *prefix)
+{
+       static const char service[] = "multipathd.service";
+       char path[PATH_MAX], file[PATH_MAX];
+       DIR *dirfd;
+       struct dirent *d;
+       int found = 0;
+
+       if (safe_sprintf(path, "%s/systemd/system", prefix))
+               return 0;
+
+       condlog(3, "%s: checking for %s in %s", dev, service, path);
+
+       dirfd = opendir(path);
+       if (dirfd == NULL)
+               return 0;
+
+       while ((d = readdir(dirfd)) != NULL) {
+               char *p;
+               struct stat stbuf;
+
+               if ((strcmp(d->d_name,".") == 0) ||
+                   (strcmp(d->d_name,"..") == 0))
+                       continue;
+
+               if (strlen(d->d_name) < 6)
+                       continue;
+
+               p = d->d_name + strlen(d->d_name) - 6;
+               if (strcmp(p, ".wants"))
+                       continue;
+               if (!safe_sprintf(file, "%s/%s/%s",
+                                 path, d->d_name, service)
+                   && stat(file, &stbuf) == 0) {
+                       condlog(3, "%s: found %s", dev, file);
+                       found++;
+                       break;
+               }
+       }
+       closedir(dirfd);
+
+       return found;
+}
+
+int systemd_service_enabled(const char *dev)
+{
+       int found = 0;
+
+       found = systemd_service_enabled_in(dev, "/etc");
+       if (!found)
+               found = systemd_service_enabled_in(dev, "/usr/lib");
+       if (!found)
+               found = systemd_service_enabled_in(dev, "/lib");
+       if (!found)
+               found = systemd_service_enabled_in(dev, "/run");
+       return found;
+}
+
+static int _linux_version_code;
+static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT;
+
+/* Returns current kernel version encoded as major*65536 + minor*256 + patch,
+ * so, for example,  to check if the kernel is greater than 2.2.11:
+ *
+ *     if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Code copied from busybox (GPLv2 or later)
+ */
+static void
+_set_linux_version_code(void)
+{
+       struct utsname name;
+       char *t;
+       int i, r;
+
+       uname(&name); /* never fails */
+       t = name.release;
+       r = 0;
+       for (i = 0; i < 3; i++) {
+               t = strtok(t, ".");
+               r = r * 256 + (t ? atoi(t) : 0);
+               t = NULL;
+       }
+       _linux_version_code = r;
+}
+
+int get_linux_version_code(void)
+{
+       pthread_once(&_lvc_initialized, _set_linux_version_code);
+       return _linux_version_code;
+}
+
+int safe_write(int fd, const void *buf, size_t count)
+{
+       while (count > 0) {
+               ssize_t r = write(fd, buf, count);
+               if (r < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -errno;
+               }
+               count -= r;
+               buf = (const char *)buf + r;
+       }
+       return 0;
+}
+
+void set_max_fds(rlim_t max_fds)
+{
+       struct rlimit fd_limit;
+
+       if (!max_fds)
+               return;
+
+       if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
+               condlog(0, "can't get open fds limit: %s",
+                       strerror(errno));
+               fd_limit.rlim_cur = 0;
+               fd_limit.rlim_max = 0;
+       }
+       if (fd_limit.rlim_cur < max_fds) {
+               fd_limit.rlim_cur = max_fds;
+               if (fd_limit.rlim_max < max_fds)
+                       fd_limit.rlim_max = max_fds;
+               if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
+                       condlog(0, "can't set open fds limit to "
+                               "%lu/%lu : %s",
+                               (unsigned long)fd_limit.rlim_cur,
+                               (unsigned long)fd_limit.rlim_max,
+                               strerror(errno));
+               } else {
+                       condlog(3, "set open fds limit to %lu/%lu",
+                               (unsigned long)fd_limit.rlim_cur,
+                               (unsigned long)fd_limit.rlim_max);
+               }
+       }
+}
+
+void free_scandir_result(struct scandir_result *res)
+{
+       int i;
+
+       for (i = 0; i < res->n; i++)
+               free(res->di[i]);
+       free(res->di);
+}
+
+void cleanup_free_ptr(void *arg)
+{
+       void **p = arg;
+
+       if (p && *p) {
+               free(*p);
+               *p = NULL;
+       }
+}
+
+void cleanup_fd_ptr(void *arg)
+{
+       int *fd = arg;
+
+       if (*fd >= 0) {
+               close(*fd);
+               *fd = -1;
+       }
+}
+
+void cleanup_mutex(void *arg)
+{
+       pthread_mutex_unlock(arg);
+}
+
+struct bitfield *alloc_bitfield(unsigned int maxbit)
+{
+       unsigned int n;
+       struct bitfield *bf;
+
+       if (maxbit == 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       n = (maxbit - 1) / bits_per_slot + 1;
+       bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
+       if (bf)
+               bf->len = maxbit;
+       return bf;
+}
+
+void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len)
+{
+       condlog(0, "%s: bitfield overflow: %u >= %u", f, bit, len);
+}
+
+int should_exit(void)
+{
+       return 0;
+}
+
+void cleanup_charp(char **p)
+{
+       free(*p);
+}
+
+void cleanup_ucharp(unsigned char **p)
+{
+       free(*p);
+}
diff --git a/libmpathutil/util.h b/libmpathutil/util.h
new file mode 100644 (file)
index 0000000..7e34c56
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+/* for rlim_t */
+#include <sys/resource.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+size_t strchop(char *);
+int basenamecpy (const char *src, char *dst, size_t size);
+int filepresent (const char *run);
+char *get_next_string(char **temp, const char *split_char);
+int get_word (const char * sentence, char ** word);
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size);
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
+int devt2devname (char *, int, const char *);
+dev_t parse_devt(const char *dev_t);
+char *convert_dev(char *dev, int is_path_device);
+void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
+int systemd_service_enabled(const char *dev);
+int get_linux_version_code(void);
+int safe_write(int fd, const void *buf, size_t count);
+void set_max_fds(rlim_t max_fds);
+int should_exit(void);
+
+#define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+#define safe_sprintf(var, format, args...)     \
+       safe_snprintf(var, sizeof(var), format, ##args)
+
+#define safe_snprintf(var, size, format, args...)      \
+       ({                                                              \
+               size_t __size = size;                                   \
+               int __ret;                                              \
+                                                                       \
+               __ret = snprintf(var, __size, format, ##args);          \
+               __ret < 0 || (size_t)__ret >= __size;                   \
+       })
+
+#define pthread_cleanup_push_cast(f, arg)              \
+       pthread_cleanup_push(((void (*)(void *))&f), (arg))
+
+void cleanup_fd_ptr(void *arg);
+void cleanup_free_ptr(void *arg);
+void cleanup_mutex(void *arg);
+
+struct scandir_result {
+       struct dirent **di;
+       int n;
+};
+void free_scandir_result(struct scandir_result *);
+
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+/*
+ * ffsll() is also available on glibc < 2.27 if _GNU_SOURCE is defined.
+ * But relying on that would require that every program using this header file
+ * set _GNU_SOURCE during compilation, because otherwise the library and the
+ * program would use different types for bitfield_t, causing errors.
+ * That's too error prone, so if in doubt, use ffs().
+ */
+#if __GLIBC_PREREQ(2, 27)
+typedef unsigned long long int bitfield_t;
+#define _ffs(x) ffsll(x)
+#else
+typedef unsigned int bitfield_t;
+#define _ffs(x) ffs(x)
+#endif
+#define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
+
+struct bitfield {
+       unsigned int len;
+       bitfield_t bits[];
+};
+
+#define STATIC_BITFIELD(name, length)                                  \
+       static struct {                                                 \
+               unsigned int len;                                       \
+               bitfield_t bits[((length) - 1) / bits_per_slot + 1];    \
+       } __static__ ## name = {                                        \
+               .len = (length),                                        \
+               .bits = { 0, },                                         \
+       }; \
+       struct bitfield *name = (struct bitfield *)& __static__ ## name
+
+struct 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)
+{
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return false;
+       }
+       return !!(bf->bits[bit / bits_per_slot] &
+                 (1ULL << (bit % bits_per_slot)));
+}
+
+static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
+{
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return;
+       }
+       bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
+}
+
+static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
+{
+       if (bit >= bf->len) {
+               log_bitfield_overflow(bit, bf->len);
+               return;
+       }
+       bf->bits[bit / bits_per_slot] &= ~(1ULL << (bit % bits_per_slot));
+}
+
+#define steal_ptr(x)                  \
+       ({                             \
+               void *___p = x;        \
+               x = NULL;              \
+               ___p;                  \
+       })
+
+void cleanup_charp(char **p);
+void cleanup_ucharp(unsigned char **p);
+#endif /* _UTIL_H */
diff --git a/libmpathutil/uxsock.c b/libmpathutil/uxsock.c
new file mode 100644 (file)
index 0000000..2135476
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Original author : tridge@samba.org, January 2002
+ *
+ * Copyright (c) 2005 Christophe Varoqui
+ * Copyright (c) 2005 Alasdair Kergon, Redhat
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef USE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+#include "mpath_cmd.h"
+
+#include "uxsock.h"
+#include "debug.h"
+
+/*
+ * Code is similar with mpath_recv_reply() with data size limitation
+ * and debug-able malloc.
+ * When limit == 0, it means no limit on data size, used for socket client
+ * to receiving data from multipathd.
+ */
+static int _recv_packet(int fd, char **buf, unsigned int timeout,
+                       ssize_t limit);
+
+/*
+ * create a unix domain socket and start listening on it
+ * return a file descriptor open on the socket
+ */
+int ux_socket_listen(const char *name)
+{
+       int fd;
+       size_t len;
+#ifdef USE_SYSTEMD
+       int num;
+#endif
+       struct sockaddr_un addr;
+
+#ifdef USE_SYSTEMD
+       num = sd_listen_fds(0);
+       if (num > 1) {
+               condlog(3, "sd_listen_fds returned %d fds", num);
+               return -1;
+       } else if (num == 1) {
+               fd = SD_LISTEN_FDS_START + 0;
+               condlog(3, "using fd %d from sd_listen_fds", fd);
+               return fd;
+       }
+#endif
+       fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+       if (fd == -1) {
+               condlog(3, "Couldn't create ux_socket, error %d", errno);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_LOCAL;
+       addr.sun_path[0] = '\0';
+       len = strlen(name) + 1;
+       if (len >= sizeof(addr.sun_path))
+               len = sizeof(addr.sun_path) - 1;
+       memcpy(&addr.sun_path[1], name, len);
+
+       len += sizeof(sa_family_t);
+       if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
+               condlog(3, "Couldn't bind to ux_socket, error %d", errno);
+               close(fd);
+               return -1;
+       }
+
+       if (listen(fd, 10) == -1) {
+               condlog(3, "Couldn't listen to ux_socket, error %d", errno);
+               close(fd);
+               return -1;
+       }
+       return fd;
+}
+
+/*
+ * send a packet in length prefix format
+ */
+int send_packet(int fd, const char *buf)
+{
+       if (mpath_send_cmd(fd, buf) < 0)
+               return -errno;
+       return 0;
+}
+
+static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit)
+{
+       int err = 0;
+       ssize_t len = 0;
+
+       *buf = NULL;
+       len = mpath_recv_reply_len(fd, timeout);
+       if (len == 0)
+               return len;
+       if (len < 0)
+               return -errno;
+       if ((limit > 0) && (len > limit))
+               return -EINVAL;
+       (*buf) = calloc(1, len);
+       if (!*buf)
+               return -ENOMEM;
+       err = mpath_recv_reply_data(fd, *buf, len, timeout);
+       if (err != 0) {
+               free(*buf);
+               (*buf) = NULL;
+               return -errno;
+       }
+       return err;
+}
+
+/*
+ * receive a packet in length prefix format
+ */
+int recv_packet(int fd, char **buf, unsigned int timeout)
+{
+       return _recv_packet(fd, buf, timeout, 0 /* no limit */);
+}
diff --git a/libmpathutil/uxsock.h b/libmpathutil/uxsock.h
new file mode 100644 (file)
index 0000000..e3d28cf
--- /dev/null
@@ -0,0 +1,7 @@
+/* some prototypes */
+int ux_socket_listen(const char *name);
+int send_packet(int fd, const char *buf);
+int recv_packet(int fd, char **buf, unsigned int timeout);
+
+#define _MAX_CMD_LEN           512
+
diff --git a/libmpathutil/vector.c b/libmpathutil/vector.c
new file mode 100644 (file)
index 0000000..df59db5
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Part:        Vector structure manipulation.
+ *
+ * Version:     $Id: vector.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
+ *
+ * Author:      Alexandre Cassen, <acassen@linux-vs.org>
+ *
+ *              This program is distributed in the hope that it will be useful,
+ *              but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *              See the GNU General Public License for more details.
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ *
+ * Copyright (c) 2002, 2003, 2004 Alexandre Cassen
+ * Copyright (c) 2005 Christophe Varoqui
+ */
+
+#include <stdlib.h>
+#include "vector.h"
+#include "msort.h"
+
+/*
+ * Initialize vector struct.
+ * allocated 'size' slot elements then return vector.
+ */
+vector
+vector_alloc(void)
+{
+       vector v = (vector) calloc(1, sizeof (struct _vector));
+       return v;
+}
+
+/* allocated one slot */
+bool
+vector_alloc_slot(vector v)
+{
+       void *new_slot = NULL;
+       int new_allocated;
+       int i;
+
+       if (!v)
+               return false;
+
+       new_allocated = v->allocated + VECTOR_DEFAULT_SIZE;
+       new_slot = realloc(v->slot, sizeof (void *) * new_allocated);
+       if (!new_slot)
+               return false;
+
+       v->slot = new_slot;
+       for (i = v->allocated; i < new_allocated; i++)
+               v->slot[i] = NULL;
+
+       v->allocated = new_allocated;
+       return true;
+}
+
+int
+vector_move_up(vector v, int src, int dest)
+{
+       void *value;
+       int i;
+       if (dest == src)
+               return 0;
+       if (dest > src || src >= v->allocated)
+               return -1;
+       value = v->slot[src];
+       for (i = src - 1; i >= dest; i--)
+               v->slot[i + 1] = v->slot[i];
+       v->slot[dest] = value;
+       return 0;
+}
+
+void *
+vector_insert_slot(vector v, int slot, void *value)
+{
+       int i;
+
+       if (!vector_alloc_slot(v))
+               return NULL;
+
+       for (i = VECTOR_SIZE(v) - 2; i >= slot; i--)
+               v->slot[i + 1] = v->slot[i];
+
+       v->slot[slot] = value;
+
+       return v->slot[slot];
+}
+
+int
+find_slot(vector v, void * addr)
+{
+       int i;
+
+       if (!v)
+               return -1;
+
+       for (i = 0; i < VECTOR_SIZE(v); i++)
+               if (v->slot[i] == addr)
+                       return i;
+
+       return -1;
+}
+
+void
+vector_del_slot(vector v, int slot)
+{
+       int i;
+
+       if (!v || !v->allocated || slot < 0 || slot >= VECTOR_SIZE(v))
+               return;
+
+       for (i = slot + 1; i < VECTOR_SIZE(v); i++)
+               v->slot[i-1] = v->slot[i];
+
+       v->allocated -= VECTOR_DEFAULT_SIZE;
+
+       if (v->allocated <= 0) {
+               free(v->slot);
+               v->slot = NULL;
+               v->allocated = 0;
+       } else {
+               void *new_slot;
+
+               new_slot = realloc(v->slot, sizeof (void *) * v->allocated);
+               if (!new_slot)
+                       v->allocated += VECTOR_DEFAULT_SIZE;
+               else
+                       v->slot = new_slot;
+       }
+}
+
+void
+vector_repack(vector v)
+{
+       int i;
+
+       if (!v || !v->allocated)
+               return;
+
+       for (i = 0; i < VECTOR_SIZE(v); i++)
+               if (i > 0 && v->slot[i] == NULL)
+                       vector_del_slot(v, i--);
+}
+
+vector
+vector_reset(vector v)
+{
+       if (!v)
+               return NULL;
+
+       if (v->slot)
+               free(v->slot);
+
+       v->allocated = 0;
+       v->slot = NULL;
+       return v;
+}
+
+/* Free memory vector allocation */
+void
+vector_free(vector v)
+{
+       if (!vector_reset(v))
+               return;
+       free(v);
+}
+
+void
+free_strvec(vector strvec)
+{
+       int i;
+       char *str;
+
+       if (!strvec)
+               return;
+
+       vector_foreach_slot (strvec, str, i)
+               if (str)
+                       free(str);
+
+       vector_free(strvec);
+}
+
+/* Set a vector slot value */
+void
+vector_set_slot(vector v, void *value)
+{
+       unsigned int i;
+
+       if (!v)
+               return;
+
+       i = VECTOR_SIZE(v) - 1;
+       v->slot[i] = value;
+}
+
+int vector_find_or_add_slot(vector v, void *value)
+{
+       int n = find_slot(v, value);
+
+       if (n >= 0)
+               return n;
+       if (!vector_alloc_slot(v))
+               return -1;
+       vector_set_slot(v, value);
+       return VECTOR_SIZE(v) - 1;
+}
+
+void vector_sort(vector v, int (*compar)(const void *, const void *))
+{
+       if (!v || !v->slot || !v->allocated)
+               return;
+       return msort((void *)v->slot, v->allocated, sizeof(void *), compar);
+
+}
diff --git a/libmpathutil/vector.h b/libmpathutil/vector.h
new file mode 100644 (file)
index 0000000..c0b09cb
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Soft:        Keepalived is a failover program for the LVS project
+ *              <www.linuxvirtualserver.org>. It monitor & manipulate
+ *              a loadbalanced server pool using multi-layer checks.
+ *
+ * Part:        vector.c include file.
+ *
+ * Version:     $Id: vector.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
+ *
+ * Author:      Alexandre Cassen, <acassen@linux-vs.org>
+ *
+ *              This program is distributed in the hope that it will be useful,
+ *              but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *              See the GNU General Public License for more details.
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _VECTOR_H
+#define _VECTOR_H
+
+#include <stdbool.h>
+
+/* vector definition */
+struct _vector {
+       int allocated;
+       void **slot;
+};
+typedef struct _vector *vector;
+
+#define VECTOR_DEFAULT_SIZE 1
+#define VECTOR_SIZE(V)   ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0)
+#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V) && (E) >= 0) ? (V)->slot[(E)] : NULL)
+#define VECTOR_LAST_SLOT(V)   (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
+
+#define vector_foreach_slot(v,p,i) \
+       for (i = 0; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
+#define vector_foreach_slot_after(v,p,i) \
+       for (; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
+#define vector_foreach_slot_backwards(v,p,i) \
+       for (i = VECTOR_SIZE(v) - 1; (int)i >= 0 && ((p) = (v)->slot[i]); i--)
+
+#define identity(x) (x)
+/*
+ * Given a vector vec with elements of given type,
+ * return a newly allocated vector with elements conv(e) for each element
+ * e in vec. "conv" may be a macro or a function.
+ * Use "identity" for a simple copy.
+ */
+#define vector_convert(new, vec, type, conv)                           \
+       ({                                                              \
+               const struct _vector *__v = (vec);                      \
+               vector __t = (new);                                     \
+               type *__j;                                              \
+               int __i;                                                \
+                                                                       \
+               if (__t == NULL)                                        \
+                       __t = vector_alloc();                           \
+               if (__t != NULL) {                                      \
+                       vector_foreach_slot(__v, __j, __i) {            \
+                               if (!vector_alloc_slot(__t)) {  \
+                                       vector_free(__t);               \
+                                       __t = NULL;                     \
+                                       break;                          \
+                               }                                       \
+                               vector_set_slot(__t, conv(__j));        \
+                       }                                               \
+               }                                                       \
+               __t;                                                    \
+       })
+
+/* Prototypes */
+extern vector vector_alloc(void);
+extern bool vector_alloc_slot(vector v);
+vector vector_reset(vector v);
+extern void vector_free(vector v);
+#define vector_free_const(x) vector_free((vector)(long)(x))
+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 vector_find_or_add_slot(vector v, void *value);
+extern void vector_repack(vector v);
+extern void vector_dump(vector v);
+extern void dump_strvec(vector strvec);
+extern int vector_move_up(vector v, int src, int dest);
+void vector_sort(vector v, int (*compar)(const void *, const void *));
+#endif
index 0a51925b416fe35140bf2a64b514f5b770fab3e3..5dbfb92362e7fd21630651edb3808142a1f1e66d 100644 (file)
@@ -5,11 +5,11 @@ DEVLIB = libmpathvalid.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmpathvalid.version
 
-CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir)
 CFLAGS += $(LIB_CFLAGS)
 
-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) \
-          -lmultipath -L$(mpathcmddir) -lmpathcmd -ludev
+LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \
+          -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd -ludev
 
 OBJS = mpath_valid.o
 
index ed06196edf1c99e6eb8b8e449288f7f74e38a0ee..3e34b1fd3142daccbcd609d73e6ce22fcc285148 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef LIB_MPATH_VALID_H
 #define LIB_MPATH_VALID_H
 
-#ifdef __cpluscplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -112,7 +112,7 @@ int mpathvalid_exit(void);
  * RETURNS:
  *     MPATH_STRICT, MPATH_SMART, MPATH_GREEDY, or MPATH_MODE_ERROR
  *
- *     MPATH_STRICT     = find_multiapths (yes|on|no|off)
+ *     MPATH_STRICT     = find_multipaths (yes|on|no|off)
  *     MPATH_SMART      = find_multipaths smart
  *     MPATH_GREEDY     = find_multipaths greedy
  *     MPATH_MODE_ERROR = multipath configuration not initialized
@@ -126,7 +126,7 @@ unsigned int mpathvalid_get_mode(void);
  *     potentially claimed (MPATH_IS_VALID, MPATH_IS_VALID_NO_CHECK,
  *     or MPATH_IS_MAYBE_VALID) and wwid is not NULL, then *wiid will
  *     be set to point to the wwid of device. If set, *wwid must be
- *     freed by the caller. path_wwids is an obptional parameter that
+ *     freed by the caller. path_wwids is an optional parameter that
  *     points to an array of wwids, that were returned from previous
  *     calls to mpathvalid_is_path(). These are wwids of existing
  *     devices that are or potentially are claimed by device-mapper
index fb03200e8a112e5a755a02e076f6cbfe76d825b4..3b60a5253e97c9733f15c9093f0bc95b8c594288 100644 (file)
@@ -8,10 +8,10 @@ DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmultipath.version
 
-CPPFLAGS += -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
+CPPFLAGS += -I$(mpathutildir) -I$(mpathcmddir) -I$(nvmedir) -D_GNU_SOURCE
 CFLAGS += $(LIB_CFLAGS)
 
-LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio
+LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd -lurcu -laio
 
 ifdef SYSTEMD
        CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
@@ -23,7 +23,7 @@ ifdef SYSTEMD
 endif
 
 ifneq ($(call check_func,dm_task_no_flush,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CPPFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
+       CPPFLAGS += -DLIBDM_API_FLUSH
 endif
 
 ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
@@ -56,15 +56,14 @@ OBJS-U := prio.o checkers.o foreign.o config.o
 OBJS-T := $(patsubst %.o,%-test.o,$(OBJS-U))
 
 # other object files
-OBJS-O := parser.o vector.o devmapper.o \
-       hwtable.o blacklist.o util.o dmparser.o \
+OBJS-O := devmapper.o hwtable.o blacklist.o dmparser.o \
        structs.o discovery.o propsel.o dict.o \
-       pgpolicies.o debug.o defaults.o uevent.o time-util.o \
-       switchgroup.o uxsock.o print.o alias.o log_pthread.o \
-       log.o configure.o structs_vec.o sysfs.o \
+       pgpolicies.o defaults.o uevent.o \
+       switchgroup.o print.o alias.o \
+       configure.o structs_vec.o sysfs.o \
        lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
        io_err_stat.o dm-generic.o generic.o nvme-lib.o \
-       libsg.o valid.o strbuf.o
+       libsg.o valid.o
 
 OBJS := $(OBJS-O) $(OBJS-U)
 
index 548a1184d361f97ef5149113388472f9c8a44285..052012248dfb226d1ea0a7c30c014f63f9dc378e 100644 (file)
@@ -573,7 +573,7 @@ static int fix_bindings_file(const struct config *conf,
                             const Bindings *bindings)
 {
        int rc;
-       long fd;
+       int fd = -1;
        char tempname[PATH_MAX];
        mode_t old_umask;
 
@@ -586,7 +586,7 @@ static int fix_bindings_file(const struct config *conf,
                return -1;
        }
        umask(old_umask);
-       pthread_cleanup_push(close_fd, (void*)fd);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
        rc = write_bindings_file(bindings, fd);
        pthread_cleanup_pop(1);
        if (rc == -1) {
@@ -672,6 +672,24 @@ static void cleanup_fclose(void *p)
        fclose(p);
 }
 
+static int alias_compar(const void *p1, const void *p2)
+{
+       const char *alias1 = (*(struct mpentry * const *)p1)->alias;
+       const char *alias2 = (*(struct mpentry * const *)p2)->alias;
+
+       if (alias1 && alias2)
+               return strcmp(alias1, alias2);
+       else
+               /* Move NULL alias to the end */
+               return alias1 ? -1 : alias2 ? 1 : 0;
+}
+
+static void cleanup_vector_free(void *arg)
+{
+       if  (arg)
+               vector_free((vector)arg);
+}
+
 /*
  * check_alias_settings(): test for inconsistent alias configuration
  *
@@ -693,12 +711,24 @@ int check_alias_settings(const struct config *conf)
        int can_write;
        int rc = 0, i, fd;
        Bindings bindings = {.allocated = 0, };
+       vector mptable = NULL;
        struct mpentry *mpe;
 
+       mptable = vector_convert(NULL, conf->mptable, struct mpentry *, identity);
+       if (!mptable)
+               return -1;
+
        pthread_cleanup_push_cast(free_bindings, &bindings);
-       vector_foreach_slot(conf->mptable, mpe, i) {
-               if (!mpe->wwid || !mpe->alias)
-                       continue;
+       pthread_cleanup_push(cleanup_vector_free, mptable);
+
+       vector_sort(mptable, alias_compar);
+       vector_foreach_slot(mptable, mpe, i) {
+               if (!mpe->alias)
+                       /*
+                        * alias_compar() sorts NULL alias at the end,
+                        * so we can stop if we encounter this.
+                        */
+                       break;
                if (add_binding(&bindings, mpe->alias, mpe->wwid) ==
                    BINDING_CONFLICT) {
                        condlog(0, "ERROR: alias \"%s\" bound to multiple wwids in multipath.conf, "
@@ -710,6 +740,7 @@ int check_alias_settings(const struct config *conf)
        }
        /* This clears the bindings */
        pthread_cleanup_pop(1);
+       pthread_cleanup_pop(1);
 
        pthread_cleanup_push_cast(free_bindings, &bindings);
        fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
index 8d8e45e5fffed3de3d10cf5cd098529968a0158b..c9a2c4caa4f6c71ab4b5fd5a8c19d368955346f3 100644 (file)
@@ -1,12 +1,13 @@
 #
 # Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@opensvc.com>
 #
+TOPDIR = ../..
 include ../../Makefile.inc
 
-CPPFLAGS += -I..
+CPPFLAGS +=  -I$(multipathdir) -I$(mpathutildir)
 CFLAGS += $(LIB_CFLAGS)
-LDFLAGS += -L.. -lmultipath
-LIBDEPS = -lmultipath -laio -lpthread -lrt
+LDFLAGS += -L$(multipathdir) -L$(mpathutildir)
+LIBDEPS = -lmultipath -lmpathutil -laio -lpthread -lrt
 
 # If you add or remove a checker also update multipath/multipath.conf.5
 LIBS= \
index bc7b7be52c1d446f1a0784aa6b9b92fdf7d667e5..2f3ece00f117d86c5e9632e4b9f46d68c7267b9a 100644 (file)
@@ -15,8 +15,8 @@
 #include <libaio.h>
 
 #include "checkers.h"
-#include "../libmultipath/debug.h"
-#include "../libmultipath/time-util.h"
+#include "debug.h"
+#include "time-util.h"
 
 #define AIO_GROUP_SIZE 1024
 
@@ -124,7 +124,7 @@ remove_aio_group(struct aio_group *aio_grp)
 
 /* If an aio_group is completely full of orphans, then no checkers can
  * use it, which means that no checkers can clear out the orphans. To
- * avoid keeping the useless group around, simply remove remove the
+ * avoid keeping the useless group around, simply remove the
  * group */
 static void
 check_orphaned_group(struct aio_group *aio_grp)
index aa636e7bc0a053e5114bcc42137073515fc1749d..04635b5c756d705886eff4877701e0c8c3e738de 100644 (file)
@@ -11,7 +11,7 @@
 #include <sys/ioctl.h>
 #include <errno.h>
 
-#include "../libmultipath/sg_include.h"
+#include "sg_include.h"
 #include "libsg.h"
 #include "checkers.h"
 #include "debug.h"
index 915918c068c6d1ded91904f0b3504eaaf57a868d..1ab7909a621e73a288e4a61ba5c3a3695e894802 100644 (file)
@@ -13,8 +13,8 @@
 
 #include "checkers.h"
 
-#include "../libmultipath/sg_include.h"
-#include "../libmultipath/unaligned.h"
+#include "sg_include.h"
+#include "unaligned.h"
 
 #define TUR_CMD_LEN            6
 #define INQUIRY_CMDLEN         6
index f7aaa30a5c2dbf9d6fc5fbf9dd7d0d49a498d6c6..9a3860fd2749f589b7c75ddd68491ec9a9e1d120 100644 (file)
@@ -14,7 +14,7 @@
 #include "checkers.h"
 #include "debug.h"
 
-#include "../libmultipath/sg_include.h"
+#include "sg_include.h"
 
 #define INQUIRY_CMDLEN         6
 #define INQUIRY_CMD            0x12
index 1bcb7576216dd27c69279a8cdb77b81b45ad96d5..551dc4f04c20e6d149ea289d8172a30e3e22d73e 100644 (file)
 
 #include "checkers.h"
 
-#include "../libmultipath/debug.h"
-#include "../libmultipath/sg_include.h"
-#include "../libmultipath/util.h"
-#include "../libmultipath/time-util.h"
+#include "debug.h"
+#include "sg_include.h"
+#include "util.h"
+#include "time-util.h"
 
 #define TUR_CMD_LEN 6
 #define HEAVY_CHECK_COUNT       10
index ab8b26e7fb7979ca8865ab65ebd3bf7eb2fd8f9c..5c5c07267aba6f5220fa865fc5be3c54de56c38c 100644 (file)
@@ -82,7 +82,7 @@ struct config *libmp_get_multipath_config(void)
 }
 
 struct config *get_multipath_config(void)
-       __attribute__((weak, alias("libmp_get_multipath_config")));
+       __attribute__((alias("libmp_get_multipath_config")));
 
 void libmp_put_multipath_config(void *conf __attribute__((unused)))
 {
@@ -90,7 +90,7 @@ void libmp_put_multipath_config(void *conf __attribute__((unused)))
 }
 
 void put_multipath_config(void *conf)
-       __attribute__((weak, alias("libmp_put_multipath_config")));
+       __attribute__((alias("libmp_put_multipath_config")));
 
 static int
 hwe_strmatch (const struct hwentry *hwe1, const struct hwentry *hwe2)
@@ -520,6 +520,14 @@ merge_mpe(struct mpentry *dst, struct mpentry *src)
        merge_num(mode);
 }
 
+static int wwid_compar(const void *p1, const void *p2)
+{
+       const char *wwid1 = (*(struct mpentry * const *)p1)->wwid;
+       const char *wwid2 = (*(struct mpentry * const *)p2)->wwid;
+
+       return strcmp(wwid1, wwid2);
+}
+
 void merge_mptable(vector mptable)
 {
        struct mpentry *mp1, *mp2;
@@ -533,10 +541,13 @@ void merge_mptable(vector mptable)
                        free_mpe(mp1);
                        continue;
                }
+       }
+       vector_sort(mptable, wwid_compar);
+       vector_foreach_slot(mptable, mp1, i) {
                j = i + 1;
                vector_foreach_slot_after(mptable, mp2, j) {
-                       if (!mp2->wwid || strcmp(mp1->wwid, mp2->wwid))
-                               continue;
+                       if (strcmp(mp1->wwid, mp2->wwid))
+                               break;
                        condlog(1, "%s: duplicate multipath config section for %s",
                                __func__, mp1->wwid);
                        merge_mpe(mp2, mp1);
index 36d4015731a5515479cf81929d95db039e0e641e..879474696528821240276189db21b6952aee7470 100644 (file)
@@ -6,6 +6,7 @@
 #include <urcu.h>
 #include <inttypes.h>
 #include "byteorder.h"
+#include "globals.h"
 
 #define ORIGIN_DEFAULT 0
 #define ORIGIN_CONFIG  1
@@ -248,7 +249,7 @@ struct config {
  * libmultipath calls. If an application wants to keep using the
  * udev variable after calling libmultipath_exit(), it should have taken
  * an additional reference on it beforehand. This is the case e.g.
- * after initiazing udev with udev_new().
+ * after initializing udev with udev_new().
  */
 extern struct udev *udev;
 
@@ -303,21 +304,8 @@ void free_config (struct config * conf);
 int init_config(const char *file);
 void uninit_config(void);
 
-/*
- * libmultipath provides default implementations of
- * get_multipath_config() and put_multipath_config().
- * Applications using these should use init_config(file, NULL)
- * to load the configuration, rather than load_config(file).
- * Likewise, uninit_config() should be used for teardown, but
- * using free_config() for that is supported, too.
- * Applications can define their own {get,put}_multipath_config()
- * functions, which override the library-internal ones, but
- * could still call libmp_{get,put}_multipath_config().
- */
 struct config *libmp_get_multipath_config(void);
-struct config *get_multipath_config(void);
 void libmp_put_multipath_config(void *);
-void put_multipath_config(void *);
 
 int parse_uid_attrs(char *uid_attrs, struct config *conf);
 const char *get_uid_attribute_by_attrs(const struct config *conf,
index 09ae708ddc87ecd12d1737dcc9f71078c4991491..8af7cd790f930da4145c086ac4bb4b9738aaf2fb 100644 (file)
@@ -397,7 +397,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
         * into a mp->params strings to feed the device-mapper
         */
        if (assemble_map(mpp, params)) {
-               condlog(0, "%s: problem assembing map", mpp->alias);
+               condlog(0, "%s: problem assembling map", mpp->alias);
                return 1;
        }
        return 0;
@@ -489,9 +489,15 @@ void trigger_partitions_udev_change(struct udev_device *dev,
 
                devtype = udev_device_get_devtype(part);
                if (devtype && !strcmp("partition", devtype)) {
+                       ssize_t ret;
+
                        condlog(4, "%s: triggering %s event for %s", __func__,
                                action, syspath);
-                       sysfs_attr_set_value(part, "uevent", action, len);
+                       ret = sysfs_attr_set_value(part, "uevent", action, len);
+                       if (ret != len)
+                               log_sysfs_attr_set_value(2, ret,
+                                       "%s: failed to trigger %s uevent",
+                                       syspath, action);
                }
                udev_device_unref(part);
        }
@@ -510,6 +516,7 @@ trigger_path_udev_change(struct path *pp, bool is_mpath)
         */
        const char *action = is_mpath ? "change" : "add";
        const char *env;
+       ssize_t len, ret;
 
        if (!pp->udev)
                return;
@@ -536,8 +543,13 @@ trigger_path_udev_change(struct path *pp, bool is_mpath)
 
        condlog(3, "triggering %s uevent for %s (is %smultipath member)",
                action, pp->dev, is_mpath ? "" : "no ");
-       sysfs_attr_set_value(pp->udev, "uevent",
-                            action, strlen(action));
+
+       len = strlen(action);
+       ret = sysfs_attr_set_value(pp->udev, "uevent", action, len);
+       if (ret != len)
+               log_sysfs_attr_set_value(2, ret,
+                                        "%s: failed to trigger %s uevent",
+                                        pp->dev, action);
        trigger_partitions_udev_change(pp->udev, action,
                                       strlen(action));
 }
@@ -568,6 +580,7 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
        struct pathgroup * pgp;
        struct path *pp;
        char buff[11];
+       ssize_t len;
        int i, j, ret, err = 0;
        struct udev_device *udd;
        int max_sectors_kb;
@@ -589,7 +602,7 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
                ret = sysfs_attr_get_value(udd, "queue/max_sectors_kb", buff,
                                           sizeof(buff));
                udev_device_unref(udd);
-               if (ret <= 0) {
+               if (!sysfs_attr_value_ok(ret, sizeof(buff))) {
                        condlog(1, "failed to get current max_sectors_kb from %s", mpp->alias);
                        return 1;
                }
@@ -600,14 +613,17 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
                }
        }
        snprintf(buff, 11, "%d", max_sectors_kb);
+       len = strlen(buff);
 
        vector_foreach_slot (mpp->pg, pgp, i) {
                vector_foreach_slot(pgp->paths, pp, j) {
                        ret = sysfs_attr_set_value(pp->udev,
                                                   "queue/max_sectors_kb",
-                                                  buff, strlen(buff));
-                       if (ret < 0) {
-                               condlog(1, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret));
+                                                  buff, len);
+                       if (ret != len) {
+                               log_sysfs_attr_set_value(1, ret,
+                                       "failed setting max_sectors_kb on %s",
+                                       pp->dev);
                                err = 1;
                        }
                }
diff --git a/libmultipath/debug.c b/libmultipath/debug.c
deleted file mode 100644 (file)
index 510e15e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2005 Christophe Varoqui
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-
-#include "log_pthread.h"
-#include <sys/types.h>
-#include <time.h>
-#include "../third-party/valgrind/drd.h"
-#include "vector.h"
-#include "config.h"
-#include "defaults.h"
-#include "debug.h"
-#include "time-util.h"
-#include "util.h"
-
-int logsink;
-int libmp_verbosity = DEFAULT_VERBOSITY;
-
-void dlog(int prio, const char * fmt, ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       if (logsink != LOGSINK_SYSLOG) {
-               if (logsink == LOGSINK_STDERR_WITH_TIME) {
-                       struct timespec ts;
-                       char buff[32];
-
-                       get_monotonic_time(&ts);
-                       safe_sprintf(buff, "%ld.%06ld",
-                                    (long)ts.tv_sec,
-                                    ts.tv_nsec/1000);
-                       fprintf(stderr, "%s | ", buff);
-               }
-               vfprintf(stderr, fmt, ap);
-       }
-       else
-               log_safe(prio + 3, fmt, ap);
-       va_end(ap);
-}
diff --git a/libmultipath/debug.h b/libmultipath/debug.h
deleted file mode 100644 (file)
index 705a5d7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef _DEBUG_H
-#define _DEBUG_H
-void dlog (int prio, const char *fmt, ...)
-       __attribute__((format(printf, 2, 3)));
-
-
-#include <pthread.h>
-#include <stdarg.h>
-
-#include "log_pthread.h"
-
-extern int logsink;
-extern int libmp_verbosity;
-
-#ifndef MAX_VERBOSITY
-#define MAX_VERBOSITY 4
-#endif
-
-enum {
-       LOGSINK_STDERR_WITH_TIME = 0,
-       LOGSINK_STDERR_WITHOUT_TIME = -1,
-       LOGSINK_SYSLOG = 1,
-};
-
-#define condlog(prio, fmt, args...)                                    \
-       do {                                                            \
-               int __p = (prio);                                       \
-                                                                       \
-               if (__p <= MAX_VERBOSITY && __p <= libmp_verbosity)     \
-                       dlog(__p, fmt "\n", ##args);                    \
-       } while (0)
-#endif /* _DEBUG_H */
index 1748d2583152678d91bc27fa71eaaf061f9ab0c9..a49db3b09cef92f3cdb792da19ee2d79e4cf4ff2 100644 (file)
@@ -1111,6 +1111,10 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
                        }
                        condlog(4, "multipath map %s removed", mapname);
                        return 0;
+               } else if (dm_is_mpath(mapname) != 1) {
+                       condlog(4, "multipath map %s removed externally",
+                               mapname);
+                       return 0; /*we raced with someone else removing it */
                } else {
                        condlog(2, "failed to remove multipath map %s",
                                mapname);
index ad049ccbbc9b32ccb3d41e02341d5e1e82101b0b..aa93fe43e6d2b447ff0fe63bb5ace6be1a7a6d8c 100644 (file)
 #include <errno.h>
 #include <inttypes.h>
 #include <libudev.h>
-#include <mpath_persist.h>
 #include "mpath_cmd.h"
 #include "dict.h"
 #include "strbuf.h"
+#include "prkey.h"
 
 static void
 do_set_int(vector strvec, void *ptr, int min, int max, const char *file,
@@ -1404,20 +1404,6 @@ set_reservation_key(vector strvec, struct be64 *be64_ptr, uint8_t *flags_ptr,
        return 0;
 }
 
-int
-print_reservation_key(struct strbuf *buff,
-                     struct be64 key, uint8_t flags, int source)
-{
-       char *flagstr = "";
-       if (source == PRKEY_SOURCE_NONE)
-               return 0;
-       if (source == PRKEY_SOURCE_FILE)
-               return append_strbuf_quoted(buff, "file");
-       if (flags & MPATH_F_APTPL_MASK)
-               flagstr = ":aptpl";
-       return print_strbuf(buff, "0x%" PRIx64 "%s", get_be64(key), flagstr);
-}
-
 static int
 def_reservation_key_handler(struct config *conf, vector strvec,
                            const char *file, int line_nr)
index d80f990a7d1562334f8c4f402d7eb0cf6d5432b6..15d9cbac471e269d4c08f4f71763b075a86120b1 100644 (file)
@@ -16,7 +16,5 @@ int print_pgpolicy(struct strbuf *buff, long v);
 int print_no_path_retry(struct strbuf *buff, long v);
 int print_undef_off_zero(struct strbuf *buff, long v);
 int print_dev_loss(struct strbuf *buff, unsigned long v);
-int print_reservation_key(struct strbuf *buff,
-                         struct be64 key, uint8_t flags, int source);
 int print_off_int_undef(struct strbuf *buff, long v);
 #endif /* _DICT_H */
index 0d8a558c416c86bff9cc17eba00d129a4761ba85..15560f8cfc0421810c8e2cf31a204be5f3f203e8 100644 (file)
@@ -263,41 +263,19 @@ declare_sysfs_get_str(vendor);
 declare_sysfs_get_str(model);
 declare_sysfs_get_str(rev);
 
-static ssize_t
-sysfs_get_binary (struct udev_device * udev, const char *attrname,
-                 unsigned char *buff, size_t len)
-{
-       ssize_t attr_len;
-       const char * devname;
-
-       if (!udev) {
-               condlog(3, "No udev device given\n");
-               return -ENOSYS;
-       }
-
-       devname = udev_device_get_sysname(udev);
-       attr_len = sysfs_bin_attr_get_value(udev, attrname, buff, len);
-       if (attr_len < 0) {
-               condlog(3, "%s: attribute %s not found in sysfs",
-                       devname, attrname);
-               return attr_len;
-       }
-       return attr_len;
-}
-
 ssize_t sysfs_get_vpd(struct udev_device * udev, unsigned char pg,
                      unsigned char *buff, size_t len)
 {
        char attrname[9];
 
        snprintf(attrname, sizeof(attrname), "vpd_pg%02x", pg);
-       return sysfs_get_binary(udev, attrname, buff, len);
+       return sysfs_bin_attr_get_value(udev, attrname, buff, len);
 }
 
 ssize_t sysfs_get_inquiry(struct udev_device * udev,
                          unsigned char *buff, size_t len)
 {
-       return sysfs_get_binary(udev, "inquiry", buff, len);
+       return sysfs_bin_attr_get_value(udev, "inquiry", buff, len);
 }
 
 int
@@ -582,10 +560,10 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen)
        if (!parent)
                return -1;
 
-       if (sysfs_attr_get_value(parent, "access_state", buff, buflen) <= 0)
+       if (!sysfs_attr_get_value_ok(parent, "access_state", buff, buflen))
                return -1;
 
-       if (sysfs_attr_get_value(parent, "preferred_path", value, 16) <= 0)
+       if (!sysfs_attr_get_value_ok(parent, "preferred_path", value, sizeof(value)))
                return 0;
 
        preferred = strtoul(value, &eptr, 0);
@@ -620,13 +598,15 @@ sysfs_set_eh_deadline(struct path *pp)
                len = sprintf(value, "%d", pp->eh_deadline);
 
        ret = sysfs_attr_set_value(hostdev, "eh_deadline",
-                                  value, len + 1);
+                                  value, len);
        /*
         * not all scsi drivers support setting eh_deadline, so failing
         * is totally reasonable
         */
-       if (ret <= 0)
-               condlog(3, "%s: failed to set eh_deadline to %s, error %d", udev_device_get_sysname(hostdev), value, -ret);
+       if (ret != len)
+               log_sysfs_attr_set_value(3, ret,
+                       "%s: failed to set eh_deadline to %s",
+                       udev_device_get_sysname(hostdev), value);
 
        udev_device_unref(hostdev);
        return (ret <= 0);
@@ -660,8 +640,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
        /*
         * read the current dev_loss_tmo value from sysfs
         */
-       ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo", value, 16);
-       if (ret <= 0) {
+       ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo", value, sizeof(value));
+       if (!sysfs_attr_value_ok(ret, sizeof(value))) {
                condlog(0, "%s: failed to read dev_loss_tmo value, "
                        "error %d", rport_id, -ret);
                goto out;
@@ -689,19 +669,22 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
            pp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
                /* Check if we need to temporarily increase dev_loss_tmo */
                if ((unsigned int)pp->fast_io_fail >= tmo) {
+                       ssize_t len;
+
                        /* Increase dev_loss_tmo temporarily */
                        snprintf(value, sizeof(value), "%u",
                                 (unsigned int)pp->fast_io_fail + 1);
+                       len = strlen(value);
                        ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
-                                                  value, strlen(value));
-                       if (ret <= 0) {
+                                                  value, len);
+                       if (ret != len) {
                                if (ret == -EBUSY)
                                        condlog(3, "%s: rport blocked",
                                                rport_id);
                                else
-                                       condlog(0, "%s: failed to set "
-                                               "dev_loss_tmo to %s, error %d",
-                                               rport_id, value, -ret);
+                                       log_sysfs_attr_set_value(0, ret,
+                                               "%s: failed to set dev_loss_tmo to %s",
+                                               rport_id, value);
                                goto out;
                        }
                }
@@ -713,32 +696,39 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                pp->dev_loss = DEFAULT_DEV_LOSS_TMO;
        }
        if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+               ssize_t len;
+
                if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
                        sprintf(value, "off");
                else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
                        sprintf(value, "0");
                else
                        snprintf(value, 16, "%u", pp->fast_io_fail);
+               len = strlen(value);
                ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo",
-                                          value, strlen(value));
-               if (ret <= 0) {
+                                          value, len);
+               if (ret != len) {
                        if (ret == -EBUSY)
                                condlog(3, "%s: rport blocked", rport_id);
                        else
-                               condlog(0, "%s: failed to set fast_io_fail_tmo to %s, error %d",
-                                       rport_id, value, -ret);
+                               log_sysfs_attr_set_value(0, ret,
+                                       "%s: failed to set fast_io_fail_tmo to %s",
+                                       rport_id, value);
                }
        }
        if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+               ssize_t len;
+
                snprintf(value, 16, "%u", pp->dev_loss);
-               ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
-                                          value, strlen(value));
-               if (ret <= 0) {
+               len = strlen(value);
+               ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, len);
+               if (ret != len) {
                        if (ret == -EBUSY)
                                condlog(3, "%s: rport blocked", rport_id);
                        else
-                               condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d",
-                                       rport_id, value, -ret);
+                               log_sysfs_attr_set_value(0, ret,
+                                       "%s: failed to set dev_loss_tmo to %s",
+                                       rport_id, value);
                }
        }
 out:
@@ -776,12 +766,16 @@ sysfs_set_session_tmo(struct path *pp)
                        condlog(3, "%s: can't set fast_io_fail_tmo to '0'"
                                "on iSCSI", pp->dev);
                } else {
+                       ssize_t len, ret;
+
                        snprintf(value, 11, "%u", pp->fast_io_fail);
-                       if (sysfs_attr_set_value(session_dev, "recovery_tmo",
-                                                value, strlen(value)) <= 0) {
-                               condlog(3, "%s: Failed to set recovery_tmo, "
-                                       " error %d", pp->dev, errno);
-                       }
+                       len = strlen(value);
+                       ret = sysfs_attr_set_value(session_dev, "recovery_tmo",
+                                                  value, len);
+                       if (ret != len)
+                               log_sysfs_attr_set_value(3, ret,
+                                       "%s: Failed to set recovery_tmo to %s",
+                                                        pp->dev, value);
                }
        }
        udev_device_unref(session_dev);
@@ -824,12 +818,16 @@ sysfs_set_nexus_loss_tmo(struct path *pp)
                pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
 
        if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+               ssize_t len, ret;
+
                snprintf(value, 11, "%u", pp->dev_loss);
-               if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
-                                        value, strlen(value)) <= 0)
-                       condlog(3, "%s: failed to update "
-                               "I_T Nexus loss timeout, error %d",
-                               pp->dev, errno);
+               len = strlen(value);
+               ret = sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
+                                          value, len);
+               if (ret != len)
+                       log_sysfs_attr_set_value(3, ret,
+                               "%s: failed to update I_T Nexus loss timeout",
+                               pp->dev);
        }
        udev_device_unref(sas_dev);
        return;
@@ -1346,7 +1344,7 @@ parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len,
                condlog(3, "HP/3PAR vendor specific VPD page length too short: %zu", in_len);
                return -EINVAL;
        }
-       if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
+       if (in[4] <= 3) /* revision must be > 3 to have Volume Name */
                return -ENODATA;
        len = get_unaligned_be32(&in[40]);
        if (len > out_len || len + 44 > in_len) {
@@ -1363,13 +1361,15 @@ static int
 get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
 {
        int len;
-       size_t buff_len;
+       ssize_t buff_len;
        unsigned char buff[VPD_BUFLEN];
 
        memset(buff, 0x0, VPD_BUFLEN);
-       if (!parent || sysfs_get_vpd(parent, pg, buff, VPD_BUFLEN) <= 0) {
-               condlog(3, "failed to read sysfs vpd pg%02x", pg);
-               return -EINVAL;
+       buff_len = sysfs_get_vpd(parent, pg, buff, VPD_BUFLEN);
+       if (buff_len < 0) {
+               condlog(3, "failed to read sysfs vpd pg%02x: %s",
+                       pg, strerror(-buff_len));
+               return buff_len;
        }
 
        if (buff[1] != pg) {
@@ -1757,8 +1757,8 @@ path_offline (struct path * pp)
        }
 
        memset(buff, 0x0, SCSI_STATE_SIZE);
-       err = sysfs_attr_get_value(parent, "state", buff, SCSI_STATE_SIZE);
-       if (err <= 0) {
+       err = sysfs_attr_get_value(parent, "state", buff, sizeof(buff));
+       if (!sysfs_attr_value_ok(err, sizeof(buff))) {
                if (err == -ENXIO)
                        return PATH_REMOVED;
                else
@@ -2162,7 +2162,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
                        return -1;
                len = sysfs_attr_get_value(pp->udev, "wwid", value,
                                           sizeof(value));
-               if (len <= 0)
+               if (!sysfs_attr_value_ok(len, sizeof(value)))
                        return -1;
                len = strlcpy(pp->wwid, value, WWID_SIZE);
                if (len >= WWID_SIZE) {
index 42cea4d42b33731e3728bddaff4e96f58494073b..d0232f209c8073ff47f3dd686b40b936aad7f3b7 100644 (file)
@@ -4,10 +4,10 @@
 TOPDIR=../..
 include ../../Makefile.inc
 
-CPPFLAGS += -I.. -I$(nvmedir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(nvmedir)
 CFLAGS += $(LIB_CFLAGS)
-LDFLAGS += -L..
-LIBDEPS = -lmultipath -ludev -lpthread -lrt
+LDFLAGS += -L$(multipathdir) -L$(mpathutildir)
+LIBDEPS = -lmultipath -lmpathutil -ludev -lpthread -lrt
 
 LIBS = libforeign-nvme.so
 
index 52ca56d89131b7336c0f413fa941aaf05bf06f33..edc9bd8b4dbc0be8a9166cbf2dc9f23d1942cad6 100644 (file)
@@ -531,14 +531,18 @@ static int _dirent_controller(const struct dirent *di)
 
 /* Find the block device for a given nvme controller */
 struct udev_device *get_ctrl_blkdev(const struct context *ctx,
-                                   struct udev_device *ctrl)
+                                   struct udev_device *ctrl, const char *ctrl_name)
 {
+       int ctrl_num, ns_num; 
        struct udev_list_entry *item;
        struct udev_device *blkdev = NULL;
        struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
        const char *devtype;
 
-       if (enm == NULL)
+       if (enm == NULL || ctrl_name == NULL)
+               return NULL;
+
+       if (sscanf(ctrl_name, "nvme%dn%d", &ctrl_num, &ns_num) != 2)
                return NULL;
 
        pthread_cleanup_push(_udev_enumerate_unref, enm);
@@ -556,6 +560,8 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
             item != NULL;
             item = udev_list_entry_get_next(item)) {
                struct udev_device *tmp;
+               const char *name = NULL ;
+               int m, n, l;
 
                tmp = udev_device_new_from_syspath(ctx->udev,
                                           udev_list_entry_get_name(item));
@@ -563,11 +569,19 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
                        continue;
 
                devtype = udev_device_get_devtype(tmp);
-               if (devtype && !strcmp(devtype, "disk")) {
+               if (devtype == NULL || strcmp(devtype, "disk")) {
+                       udev_device_unref(tmp);
+                       continue;
+               }
+
+               name = udev_device_get_sysname(tmp);
+               if (name != NULL &&
+                   sscanf(name, "nvme%dc%dn%d", &m, &n, &l) == 3 &&
+                   l == ns_num) {
                        blkdev = tmp;
                        break;
-               } else
-                       udev_device_unref(tmp);
+               }
+               udev_device_unref(tmp);
        }
 
        if (blkdev == NULL)
@@ -585,7 +599,7 @@ static void test_ana_support(struct nvme_map *map, struct udev_device *ctl)
 {
        const char *dev_t;
        char sys_path[64];
-       long fd;
+       int fd = -1;
        int rc;
 
        if (map->ana_supported != YNU_UNDEF)
@@ -601,7 +615,7 @@ static void test_ana_support(struct nvme_map *map, struct udev_device *ctl)
                return;
        }
 
-       pthread_cleanup_push(close_fd, (void *)fd);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
        rc = nvme_id_ctrl_ana(fd, NULL);
        if (rc < 0)
                condlog(2, "%s: error in nvme_id_ctrl: %s", __func__,
@@ -680,7 +694,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
                }
 
                pthread_cleanup_push(_udev_device_unref, ctrl);
-               udev = get_ctrl_blkdev(ctx, ctrl);
+               udev = get_ctrl_blkdev(ctx, ctrl, udev_device_get_sysname(map->udev));
                /*
                 * We give up the reference to the nvme device here and get
                 * it back from the child below.
index 513fa679935c4a160fff1fdc72eaa598b98d846d..2085aba5cd27962f12a357cfbaaf37620fd906a3 100644 (file)
@@ -86,7 +86,7 @@ static struct hwentry default_hw[] = {
         */
        {
                /* Generic NVMe */
-               .vendor        = "NVME",
+               .vendor        = "NVM[eE]",
                .product       = ".*",
                .uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
                .checker_name  = NONE,
@@ -350,6 +350,7 @@ static struct hwentry default_hw[] = {
                .no_path_retry = (300 / DEFAULT_CHECKINT),
                .checker_name  = EMC_CLARIION,
                .prio_name     = PRIO_EMC,
+               .detect_checker = DETECT_CHECKER_OFF,
        },
        {
                /* Invista / VPLEX */
@@ -1116,7 +1117,7 @@ static struct hwentry default_hw[] = {
        },
        {
                /* OceanStor NVMe */
-               .vendor        = "NVME",
+               .vendor        = "NVM[eE]",
                .product       = "Huawei-XSG1",
                .checker_name  = DIRECTIO,
                .no_path_retry = 12,
index b3690ac0c9ec7892eb506eba619233ce4e675ebe..8a447f7f99a3fc3704a4a382ae1f40660e401de2 100644 (file)
@@ -31,7 +31,7 @@
  *   The new version inherits the previous ones.
  */
 
-LIBMULTIPATH_15.0.0 {
+LIBMULTIPATH_16.0.0 {
 global:
        /* symbols referenced by multipath and multipathd */
        add_foreign;
@@ -42,8 +42,6 @@ global:
        alloc_path;
        alloc_path_layout;
        alloc_path_with_pathinfo;
-       alloc_strvec;
-       append_strbuf_str;
        change_foreign;
        check_alias_settings;
        check_daemon;
@@ -54,17 +52,11 @@ global:
        checker_name;
        checker_state_name;
        check_foreign;
-       cleanup_charp;
        cleanup_lock;
-       cleanup_mutex;
-       cleanup_ucharp;
-       close_fd;
        coalesce_paths;
-       convert_dev;
        count_active_paths;
        delete_all_foreign;
        delete_foreign;
-       dlog;
        dm_cancel_deferred_remove;
        dm_enablegroup;
        dm_fail_path;
@@ -99,7 +91,6 @@ global:
        find_mpe;
        find_path_by_dev;
        find_path_by_devt;
-       find_slot;
        foreign_multipath_layout;
        foreign_path_layout;
        free_config;
@@ -107,16 +98,12 @@ global:
        free_multipathvec;
        free_path;
        free_pathvec;
-       free_strvec;
-       get_monotonic_time;
        get_multipath_config;
        get_multipath_layout;
        get_path_layout;
        get_pgpolicy_id;
        get_refwwid;
        get_state;
-       get_strbuf_len;
-       get_strbuf_str;
        get_udev_device;
        get_uid;
        get_used_hwes;
@@ -129,7 +116,6 @@ global:
        init_prio;
        io_err_stat_handle_pathfail;
        is_path_valid;
-       is_quote;
        libmp_dm_task_create;
        libmp_get_version;
        libmp_get_multipath_config;
@@ -140,12 +126,7 @@ global:
        libmultipath_exit;
        libmultipath_init;
        load_config;
-       log_thread_reset;
-       log_thread_start;
-       log_thread_stop;
-       logsink;
        need_io_err_check;
-       normalize_timespec;
        orphan_path;
        parse_prkey_flags;
        pathcount;
@@ -156,10 +137,7 @@ global:
        print_all_paths;
        print_foreign_topology;
        _print_multipath_topology;
-       print_strbuf;
-       pthread_cond_init_mono;
        put_multipath_config;
-       recv_packet;
        reinstate_paths;
        remember_wwid;
        remove_map;
@@ -169,20 +147,16 @@ global:
        remove_wwid;
        replace_wwids;
        reset_checker_classes;
-       reset_strbuf;
        select_all_tg_pt;
        select_action;
        select_find_multipaths_timeout;
        select_no_path_retry;
        select_path_group;
        select_reservation_key;
-       send_packet;
-       set_max_fds;
        set_no_path_retry;
        set_path_removed;
        set_prkey;
        setup_map;
-       setup_thread_attr;
        should_multipath;
        skip_libmp_dm_init;
        snprint_blacklist_report;
@@ -204,17 +178,11 @@ global:
        stop_io_err_stat_thread;
        store_path;
        store_pathinfo;
-       strchop;
-       strlcpy;
        sync_map_state;
-       sysfs_attr_set_value;
        sysfs_get_size;
        sysfs_is_multipathed;
-       timespeccmp;
-       timespecsub;
        trigger_path_udev_change;
        trigger_paths_udev_change;
-       truncate_strbuf;
        udev;
        uevent_dispatch;
        uevent_get_dm_str;
@@ -227,14 +195,7 @@ global:
        update_multipath_table;
        update_queue_mode_add_path;
        update_queue_mode_del_path;
-       ux_socket_listen;
        valid_alias;
-       vector_alloc;
-       vector_alloc_slot;
-       vector_del_slot;
-       vector_free;
-       vector_reset;
-       vector_set_slot;
        verify_paths;
 
        /* checkers */
@@ -243,9 +204,7 @@ global:
        start_checker_thread;
 
        /* prioritizers */
-       fill_strbuf;
        get_asymmetric_access_state;
-       get_next_string;
        get_prio_timeout;
        get_target_port_group;
        get_target_port_group_support;
@@ -260,11 +219,11 @@ global:
        snprint_path_serial;
        snprint_tgt_wwnn;
        snprint_tgt_wwpn;
+       sysfs_attr_set_value;
+       sysfs_attr_get_value;
        sysfs_get_asymmetric_access_state;
 
        /* foreign */
-       free_scandir_result;
-       sysfs_attr_get_value;
 
 local:
        *;
index d7b779e773ac5af52448548a885ec2e815cd2d26..20ca77e6e7e1e501906a1eb79ccce42525e97fed 100644 (file)
@@ -2,17 +2,28 @@
 #define _LOCK_H
 
 #include <pthread.h>
+#include <urcu/uatomic.h>
+#include <stdbool.h>
 
 typedef void (wakeup_fn)(void);
 
 struct mutex_lock {
        pthread_mutex_t mutex;
        wakeup_fn *wakeup;
+       int waiters; /* uatomic access only */
 };
 
+static inline void init_lock(struct mutex_lock *a)
+{
+       pthread_mutex_init(&a->mutex, NULL);
+       uatomic_set(&a->waiters, 0);
+}
+
 static inline void lock(struct mutex_lock *a)
 {
+       uatomic_inc(&a->waiters);
        pthread_mutex_lock(&a->mutex);
+       uatomic_dec(&a->waiters);
 }
 
 static inline int trylock(struct mutex_lock *a)
@@ -30,6 +41,11 @@ static inline void __unlock(struct mutex_lock *a)
        pthread_mutex_unlock(&a->mutex);
 }
 
+static inline bool lock_has_waiters(struct mutex_lock *a)
+{
+       return (uatomic_read(&a->waiters) > 0);
+}
+
 #define lock_cleanup_pop(a) pthread_cleanup_pop(1)
 
 void cleanup_lock (void * data);
diff --git a/libmultipath/log.c b/libmultipath/log.c
deleted file mode 100644 (file)
index 45b3ed8..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (c) 2005 Christophe Varoqui
- * Copyright (c) 2005 Benjamin Marzinski, Redhat
- * Copyright (c) 2005 Jun'ichi Nomura, NEC
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <syslog.h>
-#include <time.h>
-#include <pthread.h>
-
-#include "log.h"
-#include "util.h"
-
-#define ALIGN(len, s) (((len)+(s)-1)/(s)*(s))
-
-struct logarea* la;
-static pthread_mutex_t logq_lock = PTHREAD_MUTEX_INITIALIZER;
-
-#if LOGDBG
-static void dump_logarea (void)
-{
-       struct logmsg * msg;
-
-       logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n",
-               la->start, la->end);
-       logdbg(stderr, "|addr     |next     |prio|msg\n");
-
-       for (msg = (struct logmsg *)la->head; (void *)msg != la->tail;
-            msg = msg->next)
-               logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
-                               msg->prio, (char *)&msg->str);
-
-       logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
-                       msg->prio, (char *)&msg->str);
-
-       logdbg(stderr, "\n\n");
-}
-#endif
-
-static int logarea_init (int size)
-{
-       logdbg(stderr,"enter logarea_init\n");
-       la = (struct logarea *)calloc(1, sizeof(struct logarea));
-
-       if (!la)
-               return 1;
-
-       if (size < MAX_MSG_SIZE)
-               size = DEFAULT_AREA_SIZE;
-
-       la->start = calloc(1, size);
-       if (!la->start) {
-               free(la);
-               la = NULL;
-               return 1;
-       }
-
-       la->empty = 1;
-       la->end = la->start + size;
-       la->head = la->start;
-       la->tail = la->start;
-
-       la->buff = calloc(1, MAX_MSG_SIZE + sizeof(struct logmsg));
-
-       if (!la->buff) {
-               free(la->start);
-               free(la);
-               la = NULL;
-               return 1;
-       }
-       return 0;
-
-}
-
-int log_init(char *program_name, int size)
-{
-       int ret = 1;
-
-       logdbg(stderr,"enter log_init\n");
-
-       pthread_mutex_lock(&logq_lock);
-       pthread_cleanup_push(cleanup_mutex, &logq_lock);
-
-       openlog(program_name, 0, LOG_DAEMON);
-       if (!la)
-               ret = logarea_init(size);
-
-       pthread_cleanup_pop(1);
-
-       return ret;
-}
-
-static void free_logarea (void)
-{
-       free(la->start);
-       free(la->buff);
-       free(la);
-       la = NULL;
-       return;
-}
-
-void log_close (void)
-{
-       pthread_mutex_lock(&logq_lock);
-       pthread_cleanup_push(cleanup_mutex, &logq_lock);
-
-       if (la)
-               free_logarea();
-       closelog();
-
-       pthread_cleanup_pop(1);
-       return;
-}
-
-void log_reset (char *program_name)
-{
-       pthread_mutex_lock(&logq_lock);
-       pthread_cleanup_push(cleanup_mutex, &logq_lock);
-
-       closelog();
-       openlog(program_name, 0, LOG_DAEMON);
-
-       pthread_cleanup_pop(1);
-}
-
-__attribute__((format(printf, 2, 0)))
-static int _log_enqueue(int prio, const char * fmt, va_list ap)
-{
-       int len, fwd;
-       char buff[MAX_MSG_SIZE];
-       struct logmsg * msg;
-       struct logmsg * lastmsg;
-
-       lastmsg = (struct logmsg *)la->tail;
-
-       if (!la->empty) {
-               fwd = sizeof(struct logmsg) +
-                     strlen((char *)&lastmsg->str) * sizeof(char) + 1;
-               la->tail += ALIGN(fwd, sizeof(void *));
-       }
-       vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
-       len = ALIGN(sizeof(struct logmsg) + strlen(buff) * sizeof(char) + 1,
-                   sizeof(void *));
-
-       /* not enough space on tail : rewind */
-       if (la->head <= la->tail && len > (la->end - la->tail)) {
-               logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail);
-               if (la->head == la->start ) {
-                       logdbg(stderr, "enqueue: can not rewind tail, drop msg\n");
-                       la->tail = lastmsg;
-                       return 1;  /* can't reuse */
-               }
-               la->tail = la->start;
-
-               if (la->empty)
-                       la->head = la->start;
-       }
-
-       /* not enough space on head : drop msg */
-       if (la->head > la->tail && len >= (la->head - la->tail)) {
-               logdbg(stderr, "enqueue: log area overrun, drop msg\n");
-
-               if (!la->empty)
-                       la->tail = lastmsg;
-
-               return 1;
-       }
-
-       /* ok, we can stage the msg in the area */
-       la->empty = 0;
-       msg = (struct logmsg *)la->tail;
-       msg->prio = prio;
-       memcpy((void *)&msg->str, buff, strlen(buff) + 1);
-       lastmsg->next = la->tail;
-       msg->next = la->head;
-
-       logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg, msg->next,
-               msg->prio, (char *)&msg->str);
-
-#if LOGDBG
-       dump_logarea();
-#endif
-       return 0;
-}
-
-int log_enqueue(int prio, const char *fmt, va_list ap)
-{
-       int ret = 1;
-
-       pthread_mutex_lock(&logq_lock);
-       pthread_cleanup_push(cleanup_mutex, &logq_lock);
-       if (la)
-               ret = _log_enqueue(prio, fmt, ap);
-       pthread_cleanup_pop(1);
-       return ret;
-}
-
-static int _log_dequeue(void *buff)
-{
-       struct logmsg * src = (struct logmsg *)la->head;
-       struct logmsg * dst = (struct logmsg *)buff;
-       struct logmsg * lst = (struct logmsg *)la->tail;
-
-       if (la->empty)
-               return 1;
-
-       int len = strlen((char *)&src->str) * sizeof(char) +
-                 sizeof(struct logmsg) + 1;
-
-       dst->prio = src->prio;
-       memcpy(dst, src,  len);
-
-       if (la->tail == la->head)
-               la->empty = 1; /* we purge the last logmsg */
-       else {
-               la->head = src->next;
-               lst->next = la->head;
-       }
-       logdbg(stderr, "dequeue: %p, %p, %i, %s\n",
-               (void *)src, src->next, src->prio, (char *)&src->str);
-
-       memset((void *)src, 0,  len);
-
-       return 0;
-}
-
-int log_dequeue(void *buff)
-{
-       int ret = 1;
-
-       pthread_mutex_lock(&logq_lock);
-       pthread_cleanup_push(cleanup_mutex, &logq_lock);
-       if (la)
-               ret = _log_dequeue(buff);
-       pthread_cleanup_pop(1);
-       return ret;
-}
-
-/*
- * this one can block under memory pressure
- */
-void log_syslog (void * buff)
-{
-       struct logmsg * msg = (struct logmsg *)buff;
-
-       syslog(msg->prio, "%s", (char *)&msg->str);
-}
diff --git a/libmultipath/log.h b/libmultipath/log.h
deleted file mode 100644 (file)
index fa224e4..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef LOG_H
-#define LOG_H
-
-#define DEFAULT_AREA_SIZE 16384
-#define MAX_MSG_SIZE 256
-
-#ifndef LOGLEVEL
-#define LOGLEVEL 5
-#endif
-
-#if LOGDBG
-#define logdbg(file, fmt, args...) fprintf(file, fmt, ##args)
-#else
-#define logdbg(file, fmt, args...) do {} while (0)
-#endif
-
-struct logmsg {
-       short int prio;
-       void * next;
-       char str[0];
-};
-
-struct logarea {
-       int empty;
-       void * head;
-       void * tail;
-       void * start;
-       void * end;
-       char * buff;
-};
-
-extern struct logarea* la;
-
-int log_init (char * progname, int size);
-void log_close (void);
-void log_reset (char * progname);
-int log_enqueue (int prio, const char * fmt, va_list ap)
-       __attribute__((format(printf, 2, 0)));
-int log_dequeue (void *);
-void log_syslog (void *);
-void dump_logmsg (void *);
-
-#endif /* LOG_H */
diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c
deleted file mode 100644 (file)
index 69b441b..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2005 Christophe Varoqui
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <syslog.h>
-#include <pthread.h>
-#include <sys/mman.h>
-
-#include "log_pthread.h"
-#include "log.h"
-#include "lock.h"
-#include "util.h"
-
-static pthread_t log_thr;
-
-/* logev_lock must not be taken with logq_lock held */
-static pthread_mutex_t logev_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t logev_cond = PTHREAD_COND_INITIALIZER;
-
-static int logq_running;
-static int log_messages_pending;
-
-void log_safe (int prio, const char * fmt, va_list ap)
-{
-       bool running;
-
-       if (prio > LOG_DEBUG)
-               prio = LOG_DEBUG;
-
-       /*
-        * logev_lock protects logq_running. By holding it, we avoid a race
-        * with log_thread_stop() -> log_close(), which would free the logarea.
-        */
-       pthread_mutex_lock(&logev_lock);
-       pthread_cleanup_push(cleanup_mutex, &logev_lock);
-       running = logq_running;
-
-       if (running) {
-               log_enqueue(prio, fmt, ap);
-
-               log_messages_pending = 1;
-               pthread_cond_signal(&logev_cond);
-       }
-       pthread_cleanup_pop(1);
-
-       if (!running)
-               vsyslog(prio, fmt, ap);
-}
-
-static void flush_logqueue (void)
-{
-       int empty;
-
-       do {
-               empty = log_dequeue(la->buff);
-               if (!empty)
-                       log_syslog(la->buff);
-       } while (empty == 0);
-}
-
-static void cleanup_log_thread(__attribute((unused)) void *arg)
-{
-       logdbg(stderr, "log thread exiting");
-       pthread_mutex_lock(&logev_lock);
-       logq_running = 0;
-       pthread_mutex_unlock(&logev_lock);
-}
-
-static void * log_thread (__attribute__((unused)) void * et)
-{
-       int running;
-
-       pthread_mutex_lock(&logev_lock);
-       running = logq_running;
-       if (!running)
-               logq_running = 1;
-       pthread_cond_signal(&logev_cond);
-       pthread_mutex_unlock(&logev_lock);
-       if (running)
-               /* already started */
-               return NULL;
-       pthread_cleanup_push(cleanup_log_thread, NULL);
-
-       mlockall(MCL_CURRENT | MCL_FUTURE);
-       logdbg(stderr,"enter log_thread\n");
-
-       while (1) {
-               pthread_mutex_lock(&logev_lock);
-               pthread_cleanup_push(cleanup_mutex, &logev_lock);
-               while (!log_messages_pending)
-                       /* this is a cancellation point */
-                       pthread_cond_wait(&logev_cond, &logev_lock);
-               log_messages_pending = 0;
-               pthread_cleanup_pop(1);
-
-               flush_logqueue();
-       }
-       pthread_cleanup_pop(1);
-       return NULL;
-}
-
-void log_thread_start (pthread_attr_t *attr)
-{
-       int running = 0;
-
-       logdbg(stderr,"enter log_thread_start\n");
-
-       if (log_init("multipathd", 0)) {
-               fprintf(stderr,"can't initialize log buffer\n");
-               exit(1);
-       }
-
-       pthread_mutex_lock(&logev_lock);
-       pthread_cleanup_push(cleanup_mutex, &logev_lock);
-       if (!pthread_create(&log_thr, attr, log_thread, NULL))
-               while (!(running = logq_running))
-                       pthread_cond_wait(&logev_cond, &logev_lock);
-       pthread_cleanup_pop(1);
-
-       if (!running) {
-               fprintf(stderr,"can't start log thread\n");
-               exit(1);
-       }
-
-       return;
-}
-
-void log_thread_reset (void)
-{
-       logdbg(stderr,"resetting log\n");
-       log_reset("multipathd");
-}
-
-void log_thread_stop (void)
-{
-       int running;
-
-       if (!la)
-               return;
-
-       logdbg(stderr,"enter log_thread_stop\n");
-
-       pthread_mutex_lock(&logev_lock);
-       pthread_cleanup_push(cleanup_mutex, &logev_lock);
-       running = logq_running;
-       if (running) {
-               pthread_cancel(log_thr);
-               pthread_cond_signal(&logev_cond);
-       }
-       pthread_cleanup_pop(1);
-
-       if (running)
-               pthread_join(log_thr, NULL);
-
-       flush_logqueue();
-       log_close();
-}
diff --git a/libmultipath/log_pthread.h b/libmultipath/log_pthread.h
deleted file mode 100644 (file)
index 810ac92..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _LOG_PTHREAD_H
-#define _LOG_PTHREAD_H
-
-#include <pthread.h>
-
-void log_safe(int prio, const char * fmt, va_list ap)
-       __attribute__((format(printf, 2, 0)));
-void log_thread_start(pthread_attr_t *attr);
-void log_thread_reset (void);
-void log_thread_stop(void);
-
-#endif /* _LOG_PTHREAD_H */
index a6975549fde0f5ce55d224c91770a88836b7dfd6..9fc2ae7fab4e5d74b022d73bdcdad6e5b05a61c0 100644 (file)
@@ -1035,7 +1035,7 @@ enum {
        NVME_SANITIZE_LOG_STATUS_MASK           = 0x0007,
        NVME_SANITIZE_LOG_NEVER_SANITIZED       = 0x0000,
        NVME_SANITIZE_LOG_COMPLETED_SUCCESS     = 0x0001,
-       NVME_SANITIZE_LOG_IN_PROGESS            = 0x0002,
+       NVME_SANITIZE_LOG_IN_PROGRESS           = 0x0002,
        NVME_SANITIZE_LOG_COMPLETED_FAILED      = 0x0003,
        NVME_SANITIZE_LOG_ND_COMPLETED_SUCCESS  = 0x0004,
 };
index 7e0278b5b5c1b0b23d009241b81c16520134f90e..57f82a31149d1ffbab3ec82c52c683b7a61a12b9 100644 (file)
@@ -224,7 +224,7 @@ char *nvme_char_from_block(char *block);
  * Notes:  This function does not care about transport so that the offset is
  * not going to be checked inside of this function for the unsupported fields
  * in a specific transport.  For example, BPMBL(Boot Partition Memory Buffer
- * Location) register is not supported by fabrics, but it can be chcked here.
+ * Location) register is not supported by fabrics, but it can be checked here.
  */
 static inline bool is_64bit_reg(__u32 offset)
 {
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
deleted file mode 100644 (file)
index 014d9b8..0000000
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Part:        Configuration file parser/reader. Place into the dynamic
- *              data structure representation the conf file
- *
- * Version:     $Id: parser.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
- *
- * Author:      Alexandre Cassen, <acassen@linux-vs.org>
- *
- *              This program is distributed in the hope that it will be useful,
- *              but WITHOUT ANY WARRANTY; without even the implied warranty of
- *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *              See the GNU General Public License for more details.
- *
- *              This program is free software; you can redistribute it and/or
- *              modify it under the terms of the GNU General Public License
- *              as published by the Free Software Foundation; either version
- *              2 of the License, or (at your option) any later version.
- */
-
-#include <syslog.h>
-#include <errno.h>
-
-#include "vector.h"
-#include "config.h"
-#include "parser.h"
-#include "debug.h"
-#include "strbuf.h"
-
-/* local vars */
-static int sublevel = 0;
-static int line_nr;
-
-int
-keyword_alloc(vector keywords, char *string,
-             handler_fn *handler,
-             print_fn *print,
-             int unique)
-{
-       struct keyword *keyword;
-
-       keyword = (struct keyword *)calloc(1, sizeof (struct keyword));
-
-       if (!keyword)
-               return 1;
-
-       if (!vector_alloc_slot(keywords)) {
-               free(keyword);
-               return 1;
-       }
-       keyword->string = string;
-       keyword->handler = handler;
-       keyword->print = print;
-       keyword->unique = unique;
-
-       vector_set_slot(keywords, keyword);
-
-       return 0;
-}
-
-void
-install_sublevel(void)
-{
-       sublevel++;
-}
-
-void
-install_sublevel_end(void)
-{
-       sublevel--;
-}
-
-int
-_install_keyword(vector keywords, char *string,
-                handler_fn *handler,
-                print_fn *print,
-                int unique)
-{
-       int i = 0;
-       struct keyword *keyword;
-
-       /* fetch last keyword */
-       keyword = VECTOR_LAST_SLOT(keywords);
-       if (!keyword)
-               return 1;
-
-       /* position to last sub level */
-       for (i = 0; i < sublevel; i++) {
-               keyword = VECTOR_LAST_SLOT(keyword->sub);
-               if (!keyword)
-                       return 1;
-       }
-
-       /* First sub level allocation */
-       if (!keyword->sub)
-               keyword->sub = vector_alloc();
-
-       if (!keyword->sub)
-               return 1;
-
-       /* add new sub keyword */
-       return keyword_alloc(keyword->sub, string, handler, print, unique);
-}
-
-void
-free_keywords(vector keywords)
-{
-       struct keyword *keyword;
-       int i;
-
-       if (!keywords)
-               return;
-
-       for (i = 0; i < VECTOR_SIZE(keywords); i++) {
-               keyword = VECTOR_SLOT(keywords, i);
-               if (keyword->sub)
-                       free_keywords(keyword->sub);
-               free(keyword);
-       }
-       vector_free(keywords);
-}
-
-struct keyword *
-find_keyword(vector keywords, vector v, char * name)
-{
-       struct keyword *keyword;
-       int i;
-       size_t len;
-
-       if (!name || !keywords)
-               return NULL;
-
-       if (!v)
-               v = keywords;
-
-       len = strlen(name);
-
-       for (i = 0; i < VECTOR_SIZE(v); i++) {
-               keyword = VECTOR_SLOT(v, i);
-               if ((strlen(keyword->string) == len) &&
-                   !strcmp(keyword->string, name))
-                       return keyword;
-               if (keyword->sub) {
-                       keyword = find_keyword(keywords, keyword->sub, name);
-                       if (keyword)
-                               return keyword;
-               }
-       }
-       return NULL;
-}
-
-int
-snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
-               const void *data)
-{
-       int r;
-       char *f;
-       struct config *conf;
-       STRBUF_ON_STACK(sbuf);
-
-       if (!kw || !kw->print)
-               return 0;
-
-       do {
-               f = strchr(fmt, '%');
-               if (f == NULL) {
-                       r = append_strbuf_str(&sbuf, fmt);
-                       goto out;
-               }
-               if (f != fmt &&
-                   (r = __append_strbuf_str(&sbuf, fmt, f - fmt)) < 0)
-                       goto out;
-               fmt = f + 1;
-               switch(*fmt) {
-               case 'k':
-                       if ((r = append_strbuf_str(&sbuf, kw->string)) < 0)
-                               goto out;
-                       break;
-               case 'v':
-                       conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
-                       r = kw->print(conf, &sbuf, data);
-                       pthread_cleanup_pop(1);
-                       if (r < 0)
-                               goto out;
-                       else if (r == 0) {/* no output if no value */
-                               reset_strbuf(&sbuf);
-                               goto out;
-                       }
-                       break;
-               }
-       } while (*fmt++);
-out:
-       return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
-                                  get_strbuf_len(&sbuf));
-}
-
-static const char quote_marker[] = { '\0', '"', '\0' };
-bool is_quote(const char* token)
-{
-       return token[0] == quote_marker[0] &&
-               token[1] == quote_marker[1] &&
-               token[2] == quote_marker[2];
-}
-
-vector
-alloc_strvec(char *string)
-{
-       char *cp, *start, *token;
-       int strlen;
-       int in_string;
-       vector strvec;
-
-       if (!string)
-               return NULL;
-
-       cp = string;
-
-       /* Skip white spaces */
-       while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0')
-               cp++;
-
-       /* Return if there is only white spaces */
-       if (*cp == '\0')
-               return NULL;
-
-       /* Return if string begin with a comment */
-       if (*cp == '!' || *cp == '#')
-               return NULL;
-
-       /* Create a vector and alloc each command piece */
-       strvec = vector_alloc();
-
-       if (!strvec)
-               return NULL;
-
-       in_string = 0;
-       while (1) {
-               int two_quotes = 0;
-
-               if (!vector_alloc_slot(strvec))
-                       goto out;
-
-               vector_set_slot(strvec, NULL);
-               start = cp;
-               if (*cp == '"' && !(in_string && *(cp + 1) == '"')) {
-                       cp++;
-                       token = calloc(1, sizeof(quote_marker));
-
-                       if (!token)
-                               goto out;
-
-                       memcpy(token, quote_marker, sizeof(quote_marker));
-                       if (in_string)
-                               in_string = 0;
-                       else
-                               in_string = 1;
-               } else if (!in_string && (*cp == '{' || *cp == '}')) {
-                       token = malloc(2);
-
-                       if (!token)
-                               goto out;
-
-                       *(token) = *cp;
-                       *(token + 1) = '\0';
-                       cp++;
-               } else {
-
-               move_on:
-                       while ((in_string ||
-                               (!isspace((int) *cp) && isascii((int) *cp) &&
-                                *cp != '!' && *cp != '#' && *cp != '{' &&
-                                *cp != '}')) && *cp != '\0' && *cp != '"')
-                               cp++;
-
-                       /* Two consecutive double quotes - don't end string */
-                       if (in_string && *cp == '"') {
-                               if (*(cp + 1) == '"') {
-                                       two_quotes = 1;
-                                       cp += 2;
-                                       goto move_on;
-                               }
-                       }
-
-                       strlen = cp - start;
-                       token = calloc(1, strlen + 1);
-
-                       if (!token)
-                               goto out;
-
-                       memcpy(token, start, strlen);
-                       *(token + strlen) = '\0';
-
-                       /* Replace "" by " */
-                       if (two_quotes) {
-                               char *qq = strstr(token, "\"\"");
-                               while (qq != NULL) {
-                                       memmove(qq + 1, qq + 2,
-                                               strlen + 1 - (qq + 2 - token));
-                                       qq = strstr(qq + 1, "\"\"");
-                               }
-                       }
-               }
-               vector_set_slot(strvec, token);
-
-               while ((!in_string &&
-                       (isspace((int) *cp) || !isascii((int) *cp)))
-                      && *cp != '\0')
-                       cp++;
-               if (*cp == '\0' ||
-                   (!in_string && (*cp == '!' || *cp == '#'))) {
-                       return strvec;
-               }
-       }
-out:
-       vector_free(strvec);
-       return NULL;
-}
-
-static int
-read_line(FILE *stream, char *buf, int size)
-{
-       char *p;
-
-       if (fgets(buf, size, stream) == NULL)
-               return 0;
-       strtok_r(buf, "\n\r", &p);
-       return 1;
-}
-
-void *
-set_value(vector strvec)
-{
-       char *str = VECTOR_SLOT(strvec, 1);
-       size_t size;
-       int i = 0;
-       int len = 0;
-       char *alloc = NULL;
-       char *tmp;
-
-       if (!str) {
-               condlog(0, "option '%s' missing value",
-                       (char *)VECTOR_SLOT(strvec, 0));
-               return NULL;
-       }
-       if (!is_quote(str)) {
-               size = strlen(str);
-               if (size == 0) {
-                       condlog(0, "option '%s' has empty value",
-                               (char *)VECTOR_SLOT(strvec, 0));
-                       return NULL;
-               }
-               alloc = calloc(1, sizeof (char) * (size + 1));
-               if (alloc)
-                       memcpy(alloc, str, size);
-               else
-                       goto oom;
-               return alloc;
-       }
-       /* Even empty quotes counts as a value (An empty string) */
-       alloc = (char *)calloc(1, sizeof (char));
-       if (!alloc)
-               goto oom;
-       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
-               str = VECTOR_SLOT(strvec, i);
-               if (!str) {
-                       free(alloc);
-                       condlog(0, "parse error for option '%s'",
-                               (char *)VECTOR_SLOT(strvec, 0));
-                       return NULL;
-               }
-               if (is_quote(str))
-                       break;
-               tmp = alloc;
-               /* The first +1 is for the NULL byte. The rest are for the
-                * spaces between words */
-               len += strlen(str) + 1;
-               alloc = realloc(alloc, sizeof (char) * len);
-               if (!alloc) {
-                       free(tmp);
-                       goto oom;
-               }
-               if (*alloc != '\0')
-                       strncat(alloc, " ", len - strlen(alloc));
-               strncat(alloc, str, len - strlen(alloc) - 1);
-       }
-       return alloc;
-oom:
-       condlog(0, "can't allocate memory for option '%s'",
-               (char *)VECTOR_SLOT(strvec, 0));
-       return NULL;
-}
-
-/* non-recursive configuration stream handler */
-static int kw_level = 0;
-
-int warn_on_duplicates(vector uniques, char *str, const char *file)
-{
-       char *tmp;
-       int i;
-
-       vector_foreach_slot(uniques, tmp, i) {
-               if (!strcmp(str, tmp)) {
-                       condlog(1, "%s line %d, duplicate keyword: %s",
-                               file, line_nr, str);
-                       return 0;
-               }
-       }
-       tmp = strdup(str);
-       if (!tmp)
-               return 1;
-       if (!vector_alloc_slot(uniques)) {
-               free(tmp);
-               return 1;
-       }
-       vector_set_slot(uniques, tmp);
-       return 0;
-}
-
-void free_uniques(vector uniques)
-{
-       char *tmp;
-       int i;
-
-       vector_foreach_slot(uniques, tmp, i)
-               free(tmp);
-       vector_free(uniques);
-}
-
-int
-is_sublevel_keyword(char *str)
-{
-       return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 ||
-               strcmp(str, "blacklist_exceptions") == 0 ||
-               strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 ||
-               strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 ||
-               strcmp(str, "multipath") == 0);
-}
-
-int
-validate_config_strvec(vector strvec, const char *file)
-{
-       char *str = NULL;
-       int i;
-
-       if (strvec && VECTOR_SIZE(strvec) > 0)
-               str = VECTOR_SLOT(strvec, 0);
-
-       if (str == NULL) {
-               condlog(0, "can't parse option on line %d of %s",
-                       line_nr, file);
-               return -1;
-       }
-       if (*str == '}') {
-               if (VECTOR_SIZE(strvec) > 1)
-                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file);
-               return 0;
-       }
-       if (*str == '{') {
-               condlog(0, "invalid keyword '%s' on line %d of %s",
-                       str, line_nr, file);
-               return -1;
-       }
-       if (is_sublevel_keyword(str)) {
-               str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
-               if (str == NULL)
-                       condlog(0, "missing '{' on line %d of %s",
-                               line_nr, file);
-               else if (*str != '{')
-                       condlog(0, "expecting '{' on line %d of %s. found '%s'",
-                               line_nr, file, str);
-               else if (VECTOR_SIZE(strvec) > 2)
-                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
-               return 0;
-       }
-       str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
-       if (str == NULL) {
-               condlog(0, "missing value for option '%s' on line %d of %s",
-                       (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
-               return -1;
-       }
-       if (!is_quote(str)) {
-               if (VECTOR_SIZE(strvec) > 2)
-                       condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
-               return 0;
-       }
-       for (i = 2; i < VECTOR_SIZE(strvec); i++) {
-               str = VECTOR_SLOT(strvec, i);
-               if (str == NULL) {
-                       condlog(0, "can't parse value on line %d of %s",
-                               line_nr, file);
-                       return -1;
-               }
-               if (is_quote(str)) {
-                       if (VECTOR_SIZE(strvec) > i + 1)
-                               condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file);
-                       return 0;
-               }
-       }
-       condlog(0, "missing closing quotes on line %d of %s",
-               line_nr, file);
-       return 0;
-}
-
-static int
-process_stream(struct config *conf, FILE *stream, vector keywords,
-              const char *section, const char *file)
-{
-       int i;
-       int r = 0, t;
-       struct keyword *keyword;
-       char *str;
-       char *buf;
-       vector strvec;
-       vector uniques;
-
-       uniques = vector_alloc();
-       if (!uniques)
-               return 1;
-
-       buf = calloc(1, MAXBUF);
-
-       if (!buf) {
-               vector_free(uniques);
-               return 1;
-       }
-
-       while (read_line(stream, buf, MAXBUF)) {
-               line_nr++;
-               strvec = alloc_strvec(buf);
-               if (!strvec)
-                       continue;
-
-               if (validate_config_strvec(strvec, file) != 0) {
-                       free_strvec(strvec);
-                       continue;
-               }
-
-               str = VECTOR_SLOT(strvec, 0);
-
-               if (!strcmp(str, EOB)) {
-                       if (kw_level > 0) {
-                               free_strvec(strvec);
-                               goto out;
-                       }
-                       condlog(0, "unmatched '%s' at line %d of %s",
-                               EOB, line_nr, file);
-               }
-
-               for (i = 0; i < VECTOR_SIZE(keywords); i++) {
-                       keyword = VECTOR_SLOT(keywords, i);
-
-                       if (!strcmp(keyword->string, str)) {
-                               if (keyword->unique &&
-                                   warn_on_duplicates(uniques, str, file)) {
-                                               r = 1;
-                                               free_strvec(strvec);
-                                               goto out;
-                               }
-                               if (keyword->handler) {
-                                   t = keyword->handler(conf, strvec, file,
-                                                        line_nr);
-                                       r += t;
-                                       if (t)
-                                               condlog(1, "%s line %d, parsing failed: %s",
-                                                       file, line_nr, buf);
-                               }
-
-                               if (keyword->sub) {
-                                       kw_level++;
-                                       r += process_stream(conf, stream,
-                                                           keyword->sub,
-                                                           keyword->string,
-                                                           file);
-                                       kw_level--;
-                               }
-                               break;
-                       }
-               }
-               if (i >= VECTOR_SIZE(keywords)) {
-                       if (section)
-                               condlog(1, "%s line %d, invalid keyword in the %s section: %s",
-                                       file, line_nr, section, str);
-                       else
-                               condlog(1, "%s line %d, invalid keyword: %s",
-                                       file, line_nr, str);
-               }
-               free_strvec(strvec);
-       }
-       if (kw_level == 1)
-               condlog(1, "missing '%s' at end of %s", EOB, file);
-out:
-       free(buf);
-       free_uniques(uniques);
-       return r;
-}
-
-/* Data initialization */
-int
-process_file(struct config *conf, const char *file)
-{
-       int r;
-       FILE *stream;
-
-       if (!conf->keywords) {
-               condlog(0, "No keywords allocated");
-               return 1;
-       }
-       stream = fopen(file, "r");
-       if (!stream) {
-               condlog(0, "couldn't open configuration file '%s': %s",
-                       file, strerror(errno));
-               return 1;
-       }
-
-       /* Stream handling */
-       line_nr = 0;
-       r = process_stream(conf, stream, conf->keywords, NULL, file);
-       fclose(stream);
-       //free_keywords(keywords);
-
-       return r;
-}
diff --git a/libmultipath/parser.h b/libmultipath/parser.h
deleted file mode 100644 (file)
index 11ea227..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Soft:        Keepalived is a failover program for the LVS project
- *              <www.linuxvirtualserver.org>. It monitor & manipulate
- *              a loadbalanced server pool using multi-layer checks.
- *
- * Part:        cfreader.c include file.
- *
- * Version:     $Id: parser.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
- *
- * Author:      Alexandre Cassen, <acassen@linux-vs.org>
- *
- *              This program is distributed in the hope that it will be useful,
- *              but WITHOUT ANY WARRANTY; without even the implied warranty of
- *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *              See the GNU General Public License for more details.
- *
- *              This program is free software; you can redistribute it and/or
- *              modify it under the terms of the GNU General Public License
- *              as published by the Free Software Foundation; either version
- *              2 of the License, or (at your option) any later version.
- */
-
-#ifndef _PARSER_H
-#define _PARSER_H
-
-/* system includes */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <syslog.h>
-#include <ctype.h>
-
-/* local includes */
-#include "vector.h"
-#include "config.h"
-struct strbuf;
-
-/* Global definitions */
-#define EOB  "}"
-#define MAXBUF 1024
-
-
-/* keyword definition */
-typedef int print_fn(struct config *, struct strbuf *, const void *);
-typedef int handler_fn(struct config *, vector, const char *file, int line_nr);
-
-struct keyword {
-       char *string;
-       handler_fn *handler;
-       print_fn *print;
-       vector sub;
-       int unique;
-};
-
-/* Reloading helpers */
-#define SET_RELOAD      (reload = 1)
-#define UNSET_RELOAD    (reload = 0)
-#define RELOAD_DELAY    5
-
-/* iterator helper */
-#define iterate_sub_keywords(k,p,i) \
-       for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++)
-
-/* Prototypes */
-extern int keyword_alloc(vector keywords, char *string, handler_fn *handler,
-                        print_fn *print, int unique);
-#define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1)
-extern void install_sublevel(void);
-extern void install_sublevel_end(void);
-
-extern int _install_keyword(vector keywords, char *string, handler_fn *handler,
-                           print_fn *print, int unique);
-#define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1)
-#define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0)
-extern void dump_keywords(vector keydump, int level);
-extern void free_keywords(vector keywords);
-extern vector alloc_strvec(char *string);
-extern void *set_value(vector strvec);
-extern int process_file(struct config *conf, const char *conf_file);
-extern struct keyword * find_keyword(vector keywords, vector v, char * name);
-int snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
-                   const void *data);
-bool is_quote(const char* token);
-
-#endif
index a5ab5e1db7185279174bf748222f9ee890a2dd48..400f773571bc66cadb2cfcf3444a114b2a4f6882 100644 (file)
@@ -1,12 +1,14 @@
 #
 # Copyright (C) 2007 Christophe Varoqui, <christophe.varoqui@opensvc.com>
 #
+TOPDIR=../..
+
 include ../../Makefile.inc
 
-CPPFLAGS += -I..
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir)
 CFLAGS += $(LIB_CFLAGS)
-LDFLAGS += -L..
-LIBDEPS = -lmultipath -lm -lpthread -lrt
+LDFLAGS += -L$(multipathdir) -L$(mpathutildir)
+LIBDEPS = -lmultipath -lmpathutil -lm -lpthread -lrt
 
 # If you add or remove a prioritizer also update multipath/multipath.conf.5
 LIBS = \
index 3f9c0e73e3439e2364d1c099c41565c14534568a..2db91536f3d624936ac394333d723db84d5fb4c4 100644 (file)
@@ -27,7 +27,7 @@
 #include "../structs.h"
 #include "../prio.h"
 #include "../discovery.h"
-#include "../debug.h"
+#include "debug.h"
 #include "alua_rtpg.h"
 
 #define SENSE_BUFF_LEN  32
@@ -228,25 +228,6 @@ get_target_port_group_support(const struct path *pp, unsigned int timeout)
        return rc;
 }
 
-static int
-get_sysfs_pg83(const struct path *pp, unsigned char *buff, int buflen)
-{
-       struct udev_device *parent = pp->udev;
-
-       while (parent) {
-               const char *subsys = udev_device_get_subsystem(parent);
-               if (subsys && !strncmp(subsys, "scsi", 4))
-                       break;
-               parent = udev_device_get_parent(parent);
-       }
-
-       if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) {
-               PRINT_DEBUG("failed to read sysfs vpd pg83");
-               return -1;
-       }
-       return 0;
-}
-
 int
 get_target_port_group(const struct path * pp, unsigned int timeout)
 {
@@ -265,32 +246,26 @@ get_target_port_group(const struct path * pp, unsigned int timeout)
        }
 
        memset(buf, 0, buflen);
+       rc = do_inquiry(pp, 1, 0x83, buf, buflen, timeout);
+       if (rc < 0)
+               goto out;
 
-       rc = get_sysfs_pg83(pp, buf, buflen);
-
-       if (rc < 0) {
+       scsi_buflen = get_unaligned_be16(&buf[2]) + 4;
+       if (scsi_buflen >= USHRT_MAX)
+               scsi_buflen = USHRT_MAX;
+       if (buflen < scsi_buflen) {
+               free(buf);
+               buf = (unsigned char *)malloc(scsi_buflen);
+               if (!buf) {
+                       PRINT_DEBUG("malloc failed: could not allocate"
+                                   "%u bytes", scsi_buflen);
+                       return -RTPG_RTPG_FAILED;
+               }
+               buflen = scsi_buflen;
+               memset(buf, 0, buflen);
                rc = do_inquiry(pp, 1, 0x83, buf, buflen, timeout);
                if (rc < 0)
                        goto out;
-
-               scsi_buflen = get_unaligned_be16(&buf[2]) + 4;
-               /* Paranoia */
-               if (scsi_buflen >= USHRT_MAX)
-                       scsi_buflen = USHRT_MAX;
-               if (buflen < scsi_buflen) {
-                       free(buf);
-                       buf = (unsigned char *)malloc(scsi_buflen);
-                       if (!buf) {
-                               PRINT_DEBUG("malloc failed: could not allocate"
-                                           "%u bytes", scsi_buflen);
-                               return -RTPG_RTPG_FAILED;
-                       }
-                       buflen = scsi_buflen;
-                       memset(buf, 0, buflen);
-                       rc = do_inquiry(pp, 1, 0x83, buf, buflen, timeout);
-                       if (rc < 0)
-                               goto out;
-               }
        }
 
        vpd83 = (struct vpd83_data *) buf;
index 08e992bd18556eb1693f4d8acc2499a81f8cbf41..e1a6c07113ed656506ab69b7e869b7275eacafa1 100644 (file)
@@ -143,7 +143,7 @@ inquiry_data_get_tpgs(struct inquiry_data *id)
  *-----------------------------------------------------------------------------
  */
 #define CODESET_BINARY                 0x1
-#define CODESET_ACSII                  0x2
+#define CODESET_ASCII                  0x2
 #define CODESET_UTF8                   0x3
 
 #define ASSOCIATION_UNIT               0x0
index 02dc2e2788b1917b3efd9e592baff7f88b328ee9..d1d473d461ec3494633677df42d1cd27c564a1c5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * (C) 2010 Christophe Varoqui
- * (C) 2009 Dembach Goo Infromatik GmbH & Co KG
+ * (C) 2009 Dembach Goo Informatik GmbH & Co KG
  * Manon Goo <manon.goo@dg-i.net>
  *
  * datacore.c
@@ -10,7 +10,7 @@
  * Matthias Rudolph <matthias.rudolph@hds.com>
  *
  * This work is made available on the basis of the
- * GPLv2 for detials see <http://www.gnu.org/licenses/>.
+ * GPLv2 for details see <http://www.gnu.org/licenses/>.
  *
  * Manon Goo 2009
  *
index 88cac5f010f1b16a8b6a173740081010f2864982..d569f2d722a70cdabae561d9f43360a4499e8ee5 100644 (file)
@@ -32,7 +32,7 @@
  * Half of the LUNs are accessed via one HBA/storage controller and the other
  * half via the other HBA/storage controller.
  *
- * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have
+ * In cluster environments (RAC) it also guarantees that all cluster nodes have
  * access to the LDEVs via the same controller.
  *
  * You can run the prioritizer manually in verbose mode:
index e155f6dc54112a455ee35b01c2793c1a21763a68..2f5be9b9ae5f37f1ff037767c0f1c004868f4f15 100644 (file)
@@ -64,7 +64,7 @@ static int prepare_directio_read(int fd, int *blksz, char **pbuf,
        long flags;
 
        if (ioctl(fd, BLKBSZGET, blksz) < 0) {
-               pp_pl_log(3,"catnnot get blocksize, set default");
+               pp_pl_log(3,"cannot get blocksize, set default");
                *blksz = DEF_BLK_SIZE;
        }
        if (posix_memalign((void **)pbuf, pgsize, *blksz))
@@ -193,7 +193,7 @@ out:
 }
 
 /*
- * Do not scale the prioriy in a certain range such as [0, 1024]
+ * Do not scale the priority in a certain range such as [0, 1024]
  * because scaling will eliminate the effect of base_num.
  */
 int calcPrio(double lg_avglatency, double lg_maxavglatency,
index d645f8135b174728cb3c4e6c4e98fd39fedf8521..a215499d313887f06d38f07805511d4ad005d131 100644 (file)
@@ -4,6 +4,7 @@
 #include "config.h"
 #include "util.h"
 #include "propsel.h"
+#include "strbuf.h"
 #include "prkey.h"
 #include <sys/types.h>
 #include <unistd.h>
 #include <inttypes.h>
 #include <errno.h>
 #include <libudev.h>
-#include <mpath_persist.h>
+/* MPATH_F_APTPL_MASK is publicly defined in mpath_persist.h */
+#include <../libmpathpersist/mpath_persist.h>
 
 #define PRKEY_READ 0
 #define PRKEY_WRITE 1
 
+int
+print_reservation_key(struct strbuf *buff,
+                     struct be64 key, uint8_t flags, int source)
+{
+       char *flagstr = "";
+       if (source == PRKEY_SOURCE_NONE)
+               return 0;
+       if (source == PRKEY_SOURCE_FILE)
+               return append_strbuf_quoted(buff, "file");
+       if (flags & MPATH_F_APTPL_MASK)
+               flagstr = ":aptpl";
+       return print_strbuf(buff, "0x%" PRIx64 "%s", get_be64(key), flagstr);
+}
+
+static int parse_prkey(const char *ptr, uint64_t *prkey)
+{
+       if (!ptr)
+               return 1;
+       if (*ptr == '0')
+               ptr++;
+       if (*ptr == 'x' || *ptr == 'X')
+               ptr++;
+       if (*ptr == '\0' || strlen(ptr) > 16)
+               return 1;
+       if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
+               return 1;
+       if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
+               return 1;
+       return 0;
+}
+
+int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags)
+{
+       char *flagstr;
+
+       flagstr = strchr(ptr, ':');
+       *flags = 0;
+       if (flagstr) {
+               *flagstr++ = '\0';
+               if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0)
+                       *flags = MPATH_F_APTPL_MASK;
+       }
+       return parse_prkey(ptr, prkey);
+}
+
 static int do_prkey(int fd, char *wwid, char *keystr, int cmd)
 {
        char buf[4097];
index 67391915285629b1133ba810b5e34efe80233d10..a16de1065e683df646ea833dc04f9d2a8cbe18aa 100644 (file)
@@ -13,6 +13,9 @@
 "# prkey wwid\n" \
 "#\n"
 
+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 set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
              uint8_t sa_flags);
 int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
index 50d0b5c87c72911ca33f39690c5ccdfcf55c5380..98e3aad15c8031a6a5630e496868f79289748bd2 100644 (file)
@@ -435,6 +435,7 @@ out:
 static int get_dh_state(struct path *pp, char *value, size_t value_len)
 {
        struct udev_device *ud;
+       ssize_t rc;
 
        if (pp->udev == NULL)
                return -1;
@@ -444,7 +445,10 @@ static int get_dh_state(struct path *pp, char *value, size_t value_len)
        if (ud == NULL)
                return -1;
 
-       return sysfs_attr_get_value(ud, "dh_state", value, value_len);
+       rc = sysfs_attr_get_value(ud, "dh_state", value, value_len);
+       if (!sysfs_attr_value_ok(rc, value_len))
+               return -1;
+       return rc;
 }
 
 int select_hwhandler(struct config *conf, struct multipath *mp)
@@ -1293,7 +1297,7 @@ out:
         */
        if (pp->find_multipaths_timeout < 0) {
                pp->find_multipaths_timeout = -pp->find_multipaths_timeout;
-               if (!pp->hwe) {
+               if (VECTOR_SIZE(pp->hwe) == 0) {
                        pp->find_multipaths_timeout =
                                DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT;
                        origin = "(default for unknown hardware)";
diff --git a/libmultipath/strbuf.c b/libmultipath/strbuf.c
deleted file mode 100644 (file)
index f654594..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 2021 SUSE LLC
- * SPDX-License-Identifier: GPL-2.0-only
- */
-#include <inttypes.h>
-#include <stdint.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <assert.h>
-#include "strbuf.h"
-
-static const char empty_str[] = "";
-
-char *__get_strbuf_buf(struct strbuf *buf)
-{
-       return buf->buf;
-}
-
-const char *get_strbuf_str(const struct strbuf *buf)
-{
-       return buf->buf ? buf->buf : empty_str;
-}
-
-char *steal_strbuf_str(struct strbuf *buf)
-{
-       char *p = buf->buf;
-
-       buf->buf = NULL;
-       buf->size = buf->offs = 0;
-       return p;
-}
-
-size_t get_strbuf_len(const struct strbuf *buf)
-{
-       return buf->offs;
-}
-
-static bool strbuf_is_sane(const struct strbuf *buf)
-{
-       return buf && ((!buf->buf && !buf->size && !buf->offs) ||
-                      (buf->buf && buf->size && buf->size > buf->offs));
-}
-
-void reset_strbuf(struct strbuf *buf)
-{
-       free(buf->buf);
-       buf->buf = NULL;
-       buf->size = buf->offs = 0;
-}
-
-void free_strbuf(struct strbuf *buf)
-{
-       if (!buf)
-               return;
-       reset_strbuf(buf);
-       free(buf);
-}
-
-struct strbuf *new_strbuf(void)
-{
-       return calloc(1, sizeof(struct strbuf));
-}
-
-int truncate_strbuf(struct strbuf *buf, size_t offs)
-{
-       if (!buf->buf)
-               return -EFAULT;
-       if (offs > buf->offs)
-               return -ERANGE;
-
-       buf->offs = offs;
-       buf->buf[offs] = '\0';
-       return 0;
-}
-
-#define BUF_CHUNK 64
-
-static int expand_strbuf(struct strbuf *buf, int addsz)
-{
-       size_t add;
-       char *tmp;
-
-       assert(strbuf_is_sane(buf));
-       if (addsz < 0)
-               return -EINVAL;
-       if (buf->size - buf->offs >= (size_t)addsz + 1)
-               return 0;
-
-       add = ((addsz - (buf->size - buf->offs)) / BUF_CHUNK + 1)
-               * BUF_CHUNK;
-
-       if (buf->size >= SIZE_MAX - add) {
-               add = SIZE_MAX - buf->size;
-               if (add < (size_t)addsz + 1)
-                       return -EOVERFLOW;
-       }
-
-       tmp = realloc(buf->buf, buf->size + add);
-       if (!tmp)
-               return -ENOMEM;
-
-       buf->buf = tmp;
-       buf->size += add;
-       buf->buf[buf->offs] = '\0';
-
-       return 0;
-}
-
-int __append_strbuf_str(struct strbuf *buf, const char *str, int slen)
-{
-       int ret;
-
-       if ((ret = expand_strbuf(buf, slen)) < 0)
-               return ret;
-
-       memcpy(buf->buf + buf->offs, str, slen);
-       buf->offs += slen;
-       buf->buf[buf->offs] = '\0';
-
-       return slen;
-}
-
-int append_strbuf_str(struct strbuf *buf, const char *str)
-{
-       size_t slen;
-
-       if (!str)
-               return -EINVAL;
-
-       slen = strlen(str);
-       if (slen > INT_MAX)
-               return -ERANGE;
-
-       return __append_strbuf_str(buf, str, slen);
-}
-
-int fill_strbuf(struct strbuf *buf, char c, int slen)
-{
-       int ret;
-
-       if ((ret = expand_strbuf(buf, slen)) < 0)
-               return ret;
-
-       memset(buf->buf + buf->offs, c, slen);
-       buf->offs += slen;
-       buf->buf[buf->offs] = '\0';
-
-       return slen;
-}
-
-int append_strbuf_quoted(struct strbuf *buff, const char *ptr)
-{
-       char *quoted, *q;
-       const char *p;
-       unsigned n_quotes, i;
-       size_t qlen;
-       int ret;
-
-       if (!ptr)
-               return -EINVAL;
-
-       for (n_quotes = 0, p = strchr(ptr, '"'); p; p = strchr(++p, '"'))
-               n_quotes++;
-
-       /* leading + trailing quote, 1 extra quote for every quote in ptr */
-       qlen = strlen(ptr) + 2 + n_quotes;
-       if (qlen > INT_MAX)
-               return -ERANGE;
-       if ((ret = expand_strbuf(buff, qlen)) < 0)
-               return ret;
-
-       quoted = &(buff->buf[buff->offs]);
-       *quoted++ = '"';
-       for (p = ptr, q = quoted, i = 0; i < n_quotes; i++) {
-               char *q1 = memccpy(q, p, '"', qlen - 2 - (q - quoted));
-
-               assert(q1 != NULL);
-               p += q1 - q;
-               *q1++ = '"';
-               q = q1;
-       }
-       q = mempcpy(q, p, qlen - 2 - (q - quoted));
-       *q++ = '"';
-       *q = '\0';
-       ret = q - &(buff->buf[buff->offs]);
-       buff->offs += ret;
-       return ret;
-}
-
-__attribute__((format(printf, 2, 3)))
-int print_strbuf(struct strbuf *buf, const char *fmt, ...)
-{
-       va_list ap;
-       int ret;
-       char *tail;
-
-       va_start(ap, fmt);
-       ret = vasprintf(&tail, fmt, ap);
-       va_end(ap);
-
-       if (ret < 0)
-               return -ENOMEM;
-
-       ret = __append_strbuf_str(buf, tail, ret);
-
-       free(tail);
-       return ret;
-}
diff --git a/libmultipath/strbuf.h b/libmultipath/strbuf.h
deleted file mode 100644 (file)
index 41d7d54..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2021 SUSE LLC
- * SPDX-License-Identifier: GPL-2.0-only
- */
-#ifndef _STRBUF_H
-#define _STRBUF_H
-#include <errno.h>
-#include <string.h>
-
-struct strbuf {
-       char *buf;
-       size_t size;
-       size_t offs;
-};
-
-/**
- * reset_strbuf(): prepare strbuf for new content
- * @param strbuf: string buffer to reset
- *
- * Frees internal buffer and resets size and offset to 0.
- * Can be used to cleanup a struct strbuf on stack.
- */
-void reset_strbuf(struct strbuf *buf);
-
-/**
- * free_strbuf(): free resources
- * @param strbuf: string buffer to discard
- *
- * Frees all memory occupied by a struct strbuf.
- */
-void free_strbuf(struct strbuf *buf);
-
-/**
- * macro: STRBUF_INIT
- *
- * Use this to initialize a local struct strbuf on the stack,
- * or in a global/static variable.
- */
-#define STRBUF_INIT { .buf = NULL, }
-
-/**
- * macro: STRBUF_ON_STACK
- *
- * Define and initialize a local struct @strbuf to be cleaned up when
- * the current scope is left
- */
-#define STRBUF_ON_STACK(__x)                                           \
-       struct strbuf __attribute__((cleanup(reset_strbuf))) (__x) = STRBUF_INIT;
-
-/**
- * new_strbuf(): allocate a struct strbuf on the heap
- *
- * @returns: pointer to allocated struct, or NULL in case of error.
- */
-struct strbuf *new_strbuf(void);
-
-/**
- * get_strbuf_buf(): retrieve a pointer to the strbuf's buffer
- * @param buf: a struct strbuf
- * @returns: pointer to the string written to the strbuf so far.
- *
- * INTERNAL ONLY.
- * DANGEROUS: Unlike the return value of get_strbuf_str(),
- * this string can be written to, modifying the strbuf's content.
- * USE WITH CAUTION.
- * If @strbuf was never written to, the function returns NULL.
- * The return value of this function must not be free()d.
- */
-char *__get_strbuf_buf(struct strbuf *buf);
-
-/**
- * get_strbuf_str(): retrieve string from strbuf
- * @param buf: a struct strbuf
- * @returns: pointer to the string written to the strbuf so far.
- *
- * If @strbuf was never written to, the function returns a zero-
- * length string. The return value of this function must not be
- * free()d.
- */
-const char *get_strbuf_str(const struct strbuf *buf);
-
-/**
- * steal_strbuf_str(): retrieve string from strbuf and reset
- * @param buf: a struct strbuf
- * @returns: pointer to the string written to @strbuf, or NULL
- *
- * After calling this function, the @strbuf is empty as if freshly
- * initialized. The caller is responsible to free() the returned pointer.
- * If @strbuf was never written to (not even an empty string was appended),
- * the function returns NULL.
- */
-char *steal_strbuf_str(struct strbuf *buf);
-
-/**
- * get_strbuf_len(): retrieve string length from strbuf
- * @param buf: a struct strbuf
- * @returns: the length of the string written to @strbuf so far.
- */
-size_t get_strbuf_len(const struct strbuf *buf);
-
-/**
- * truncate_strbuf(): shorten the buffer
- * @param buf: struct strbuf to truncate
- * @param offs: new buffer position / offset
- * @returns: 0 on success, negative error code otherwise.
- *
- * If @strbuf is freshly allocated/reset (never written to), -EFAULT
- * is returned. if @offs must be higher than the current offset as returned
- * by get_strbuf_len(), -ERANGE is returned. The allocated size of the @strbuf
- * remains unchanged.
- */
-int truncate_strbuf(struct strbuf *buf, size_t offs);
-
-/**
- * __append_strbuf_str(): append string of known length
- * @param buf: the struct strbuf to write to
- * @param str: the string to append, not necessarily 0-terminated
- * @param slen: max number of characters to append, must be non-negative
- * @returns: @slen = number of appended characters if successful (excluding
- * terminating '\0'); negative error code otherwise.
- *
- * Notes: a 0-byte is always appended to the output buffer after @slen characters.
- * 0-bytes possibly contained in the first @slen characters are copied into
- * the output. If the function returns an error, @strbuf is unchanged.
- */
-int __append_strbuf_str(struct strbuf *buf, const char *str, int slen);
-
-/**
- * append_strbuf_str(): append string
- * @param buf: the struct strbuf to write to
- * @param str: the string to append, 0-terminated
- * @returns: number of appended characters if successful (excluding
- * terminating '\0'); negative error code otherwise
- *
- * Appends the given 0-terminated string to @strbuf, expanding @strbuf's size
- * as necessary. If the function returns an error, @strbuf is unchanged.
- */
-int append_strbuf_str(struct strbuf *buf, const char *str);
-
-/**
- * fill_strbuf_str(): pad strbuf with a character
- * @param buf: the struct strbuf to write to
- * @param c: the character used for filling
- * @param slen: max number of characters to append, must be non-negative
- * @returns: number of appended characters if successful (excluding
- * terminating '\0'); negative error code otherwise
- *
- * Appends the given character @slen times to @strbuf, expanding @strbuf's size
- * as necessary. If the function returns an error, @strbuf is unchanged.
- */
-int fill_strbuf(struct strbuf *buf, char c, int slen);
-
-/**
- * append_strbuf_quoted(): append string in double quotes, escaping quotes in string
- * @param buf: the struct strbuf to write to
- * @param str: the string to append, 0-terminated
- * @returns: number of appended characters if successful (excluding
- * terminating '\0'); negative error code otherwise
- *
- * Appends the given string to @strbuf, with leading and trailing double
- * quotes (") added, expanding @strbuf's size as necessary. Any double quote
- * characters (") in the string are transformed to double double quotes ("").
- * If the function returns an error, @strbuf is unchanged.
- */
-int append_strbuf_quoted(struct strbuf *buf, const char *str);
-
-/**
- * print_strbuf(): print to strbuf, formatted
- * @param buf: the struct strbuf to print to
- * @param fmt: printf()-like format string
- * @returns: number of appended characters if successful, (excluding
- * terminating '\0'); negative error code otherwise
- *
- * Appends the the arguments following @fmt, formatted as in printf(), to
- * @strbuf, expanding @strbuf's size as necessary. The function makes sure that
- * the output @strbuf is always 0-terminated.
- * If the function returns an error, @strbuf is unchanged.
- */
-__attribute__((format(printf, 2, 3)))
-int print_strbuf(struct strbuf *buf, const char *fmt, ...);
-
-#endif
index a6a09441de2673d81c6261be1d99f0b7a11cac27..5a713d464912f9a5f135f4c944d7583c5474be30 100644 (file)
@@ -17,7 +17,6 @@
 #define FILE_NAME_SIZE         256
 #define CALLOUT_MAX_SIZE       256
 #define BLK_DEV_SIZE           33
-#define PATH_SIZE              512
 #define NAME_SIZE              512
 #define HOST_NAME_LEN          16
 #define SLOT_NAME_SIZE         40
@@ -351,6 +350,7 @@ struct path {
        int fast_io_fail;
        unsigned int dev_loss;
        int eh_deadline;
+       bool is_checked;
        /* configlet pointers */
        vector hwe;
        struct gen_path generic_path;
@@ -519,6 +519,4 @@ int pathcmp (const struct pathgroup *, const struct pathgroup *);
 int add_feature (char **, const char *);
 int remove_feature (char **, const char *);
 
-extern char sysfs_path[PATH_SIZE];
-
 #endif /* _STRUCTS_H */
index a69f0643512b54186812af9a3c00f5fa37555c8a..645896c6dc8316d40fc4166ffd3bc75e428ec786 100644 (file)
@@ -205,7 +205,7 @@ static bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
                                continue;
 
                        /*
-                        * At this point, pp->udev is valid and and pp->wwid
+                        * At this point, pp->udev is valid and pp->wwid
                         * is the best we could get
                         */
                        if (*pp->wwid && strcmp(mpp->wwid, pp->wwid)) {
index f45dbee1069c38946cc0077ff8bb610267a748ad..afde84940a881648fca5890f67eb5ab472eb9b2a 100644 (file)
  * as libudev lacks the capability to update an attribute value.
  * So for modified attributes we need to implement our own function.
  */
-ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
-                            char * value, size_t value_len)
+static ssize_t __sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
+                                     char *value, size_t value_len, bool binary)
 {
-       char devpath[PATH_SIZE];
-       struct stat statbuf;
-       int fd;
+       const char *syspath;
+       char devpath[PATH_MAX];
+       int fd = -1;
        ssize_t size = -1;
 
-       if (!dev || !attr_name || !value)
-               return 0;
+       if (!dev || !attr_name || !value || !value_len) {
+               condlog(1, "%s: invalid parameters", __func__);
+               return -EINVAL;
+       }
 
-       snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
-                attr_name);
+       syspath = udev_device_get_syspath(dev);
+       if (!syspath) {
+               condlog(3, "%s: invalid udevice", __func__);
+               return -EINVAL;
+       }
+       if (safe_sprintf(devpath, "%s/%s", syspath, attr_name)) {
+               condlog(3, "%s: devpath overflow", __func__);
+               return -EOVERFLOW;
+       }
        condlog(4, "open '%s'", devpath);
        /* read attribute value */
        fd = open(devpath, O_RDONLY);
        if (fd < 0) {
-               condlog(4, "attribute '%s' can not be opened: %s",
-                       devpath, strerror(errno));
+               condlog(3, "%s: attribute '%s' can not be opened: %s",
+                       __func__, devpath, strerror(errno));
                return -errno;
        }
-       if (fstat(fd, &statbuf) < 0) {
-               condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
-               close(fd);
-               return -ENXIO;
-       }
-       /* skip directories */
-       if (S_ISDIR(statbuf.st_mode)) {
-               condlog(4, "%s is a directory", devpath);
-               close(fd);
-               return -EISDIR;
-       }
-       /* skip non-writeable files */
-       if ((statbuf.st_mode & S_IRUSR) == 0) {
-               condlog(4, "%s is not readable", devpath);
-               close(fd);
-               return -EPERM;
-       }
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
 
        size = read(fd, value, value_len);
        if (size < 0) {
-               condlog(4, "read from %s failed: %s", devpath, strerror(errno));
                size = -errno;
-               value[0] = '\0';
-       } else if (size == (ssize_t)value_len) {
+               condlog(3, "%s: read from %s failed: %s", __func__, devpath,
+                       strerror(errno));
+               if (!binary)
+                       value[0] = '\0';
+       } else if (!binary && size == (ssize_t)value_len) {
+               condlog(3, "%s: overflow reading from %s (required len: %zu)",
+                       __func__, devpath, size);
                value[size - 1] = '\0';
-               condlog(4, "overflow while reading from %s", devpath);
-               size = 0;
-       } else {
+       } else if (!binary) {
                value[size] = '\0';
                size = strchop(value);
        }
 
-       close(fd);
+       pthread_cleanup_pop(1);
        return size;
 }
 
-ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
-                                unsigned char * value, size_t value_len)
+ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
+                            char *value, size_t value_len)
 {
-       char devpath[PATH_SIZE];
-       struct stat statbuf;
-       int fd;
-       ssize_t size = -1;
-
-       if (!dev || !attr_name || !value)
-               return 0;
-
-       snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
-                attr_name);
-       condlog(4, "open '%s'", devpath);
-       /* read attribute value */
-       fd = open(devpath, O_RDONLY);
-       if (fd < 0) {
-               condlog(4, "attribute '%s' can not be opened: %s",
-                       devpath, strerror(errno));
-               return -errno;
-       }
-       if (fstat(fd, &statbuf) != 0) {
-               condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
-               close(fd);
-               return -ENXIO;
-       }
-
-       /* skip directories */
-       if (S_ISDIR(statbuf.st_mode)) {
-               condlog(4, "%s is a directory", devpath);
-               close(fd);
-               return -EISDIR;
-       }
-
-       /* skip non-writeable files */
-       if ((statbuf.st_mode & S_IRUSR) == 0) {
-               condlog(4, "%s is not readable", devpath);
-               close(fd);
-               return -EPERM;
-       }
-
-       size = read(fd, value, value_len);
-       if (size < 0) {
-               condlog(4, "read from %s failed: %s", devpath, strerror(errno));
-               size = -errno;
-       } else if (size == (ssize_t)value_len) {
-               condlog(4, "overflow while reading from %s", devpath);
-               size = 0;
-       }
+       return __sysfs_attr_get_value(dev, attr_name, value, value_len, false);
+}
 
-       close(fd);
-       return size;
+ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
+                                unsigned char *value, size_t value_len)
+{
+       return __sysfs_attr_get_value(dev, attr_name, (char *)value,
+                                     value_len, true);
 }
 
 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
                             const char * value, size_t value_len)
 {
-       char devpath[PATH_SIZE];
-       struct stat statbuf;
-       int fd;
+       const char *syspath;
+       char devpath[PATH_MAX];
+       int fd = -1;
        ssize_t size = -1;
 
-       if (!dev || !attr_name || !value || !value_len)
-               return 0;
+       if (!dev || !attr_name || !value || !value_len) {
+               condlog(1, "%s: invalid parameters", __func__);
+               return -EINVAL;
+       }
+
+       syspath = udev_device_get_syspath(dev);
+       if (!syspath) {
+               condlog(3, "%s: invalid udevice", __func__);
+               return -EINVAL;
+       }
+       if (safe_sprintf(devpath, "%s/%s", syspath, attr_name)) {
+               condlog(3, "%s: devpath overflow", __func__);
+               return -EOVERFLOW;
+       }
 
-       snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
-                attr_name);
        condlog(4, "open '%s'", devpath);
        /* write attribute value */
        fd = open(devpath, O_WRONLY);
        if (fd < 0) {
-               condlog(4, "attribute '%s' can not be opened: %s",
-                       devpath, strerror(errno));
-               return -errno;
-       }
-       if (fstat(fd, &statbuf) != 0) {
-               condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
-               close(fd);
+               condlog(3, "%s: attribute '%s' can not be opened: %s",
+                       __func__, devpath, strerror(errno));
                return -errno;
        }
-
-       /* skip directories */
-       if (S_ISDIR(statbuf.st_mode)) {
-               condlog(4, "%s is a directory", devpath);
-               close(fd);
-               return -EISDIR;
-       }
-
-       /* skip non-writeable files */
-       if ((statbuf.st_mode & S_IWUSR) == 0) {
-               condlog(4, "%s is not writeable", devpath);
-               close(fd);
-               return -EPERM;
-       }
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
 
        size = write(fd, value, value_len);
        if (size < 0) {
-               condlog(4, "write to %s failed: %s", devpath, strerror(errno));
                size = -errno;
-       } else if (size < (ssize_t)value_len) {
-               condlog(4, "tried to write %ld to %s. Wrote %ld",
-                       (long)value_len, devpath, (long)size);
-               size = 0;
-       }
+               condlog(3, "%s: write to %s failed: %s", __func__, 
+                       devpath, strerror(errno));
+       } else if (size < (ssize_t)value_len)
+               condlog(3, "%s: underflow writing %zu bytes to %s. Wrote %zd bytes",
+                       __func__, value_len, devpath, size);
 
-       close(fd);
+       pthread_cleanup_pop(1);
        return size;
 }
 
@@ -220,7 +165,7 @@ sysfs_get_size (struct path *pp, unsigned long long * size)
                return 1;
 
        attr[0] = '\0';
-       if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) {
+       if (!sysfs_attr_get_value_ok(pp->udev, "size", attr, sizeof(attr))) {
                condlog(3, "%s: No size attribute in sysfs", pp->dev);
                return 1;
        }
@@ -239,7 +184,7 @@ sysfs_get_size (struct path *pp, unsigned long long * size)
 int sysfs_check_holders(char * check_devt, char * new_devt)
 {
        unsigned int major, new_minor, table_minor;
-       char path[PATH_MAX], check_dev[PATH_SIZE];
+       char path[PATH_MAX], check_dev[FILE_NAME_SIZE];
        char * table_name;
        DIR *dirfd;
        struct dirent *holder;
@@ -249,7 +194,7 @@ int sysfs_check_holders(char * check_devt, char * new_devt)
                return 0;
        }
 
-       if (devt2devname(check_dev, PATH_SIZE, check_devt)) {
+       if (devt2devname(check_dev, sizeof(check_dev), check_devt)) {
                condlog(1, "can't get devname for %s", check_devt);
                return 0;
        }
@@ -327,7 +272,7 @@ bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
        sr.n = r;
        pthread_cleanup_push_cast(free_scandir_result, &sr);
        for (i = 0; i < r && !found; i++) {
-               long fd;
+               int fd = -1;
                int nr;
                char uuid[WWID_SIZE + UUID_PREFIX_LEN];
 
@@ -341,7 +286,7 @@ bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
                        continue;
                }
 
-               pthread_cleanup_push(close_fd, (void *)fd);
+               pthread_cleanup_push(cleanup_fd_ptr, &fd);
                nr = read(fd, uuid, sizeof(uuid));
                if (nr > (int)UUID_PREFIX_LEN &&
                    !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) {
index 72b39ab2eb573a95ebba3ae04bd2e2475c72d0c8..799f68e933fac0a43edd26afe70944b95a12e9dd 100644 (file)
@@ -5,6 +5,7 @@
 #ifndef _LIBMULTIPATH_SYSFS_H
 #define _LIBMULTIPATH_SYSFS_H
 #include <stdbool.h>
+#include "strbuf.h"
 
 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
                             const char * value, size_t value_len);
@@ -12,6 +13,28 @@ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
                             char * value, size_t value_len);
 ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
                                 unsigned char * value, size_t value_len);
+#define sysfs_attr_value_ok(rc, value_len)                     \
+       ({                                                      \
+               ssize_t __r = rc;                               \
+               __r >= 0 && (size_t)__r < (size_t)value_len;    \
+       })
+
+#define sysfs_attr_get_value_ok(dev, attr, val, len) \
+       ({ \
+               size_t __l = (len);                                     \
+               ssize_t __rc = sysfs_attr_get_value(dev, attr, val, __l); \
+               sysfs_attr_value_ok(__rc, __l); \
+       })
+
+#define log_sysfs_attr_set_value(prio, rc, fmt, __args...)             \
+do {                                                                   \
+       STRBUF_ON_STACK(__buf);                                         \
+       if (print_strbuf(&__buf, fmt, ##__args) >= 0 &&                 \
+           print_strbuf(&__buf, ": %s", rc < 0 ? strerror(-rc) :       \
+                                       "write underflow") >= 0)        \
+               condlog(prio, "%s", get_strbuf_str(&__buf));            \
+} while(0)
+
 int sysfs_get_size (struct path *pp, unsigned long long * size);
 int sysfs_check_holders(char * check_devt, char * new_devt);
 bool sysfs_is_multipathed(struct path *pp, bool set_wwid);
diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c
deleted file mode 100644 (file)
index 2919300..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <assert.h>
-#include <pthread.h>
-#include <time.h>
-#include "time-util.h"
-
-void get_monotonic_time(struct timespec *res)
-{
-       struct timespec ts;
-       int rv = clock_gettime(CLOCK_MONOTONIC, &ts);
-
-       assert(rv == 0);
-       *res = ts;
-}
-
-/* Initialize @cond as a condition variable that uses the monotonic clock */
-void pthread_cond_init_mono(pthread_cond_t *cond)
-{
-       pthread_condattr_t attr;
-       int res;
-
-       res = pthread_condattr_init(&attr);
-       assert(res == 0);
-       res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
-       assert(res == 0);
-       res = pthread_cond_init(cond, &attr);
-       assert(res == 0);
-       res = pthread_condattr_destroy(&attr);
-       assert(res == 0);
-}
-
-/* Ensure that 0 <= ts->tv_nsec && ts->tv_nsec < 1000 * 1000 * 1000. */
-void normalize_timespec(struct timespec *ts)
-{
-       while (ts->tv_nsec < 0) {
-               ts->tv_nsec += 1000L * 1000 * 1000;
-               ts->tv_sec--;
-       }
-       while (ts->tv_nsec >= 1000L * 1000 * 1000) {
-               ts->tv_nsec -= 1000L * 1000 * 1000;
-               ts->tv_sec++;
-       }
-}
-
-/* Compute *res = *a - *b */
-void timespecsub(const struct timespec *a, const struct timespec *b,
-                struct timespec *res)
-{
-       res->tv_sec = a->tv_sec - b->tv_sec;
-       res->tv_nsec = a->tv_nsec - b->tv_nsec;
-       normalize_timespec(res);
-}
-
-int timespeccmp(const struct timespec *a, const struct timespec *b)
-{
-       struct timespec tmp;
-
-       timespecsub(a, b, &tmp);
-       if (tmp.tv_sec > 0)
-               return 1;
-       if (tmp.tv_sec < 0)
-               return -1;
-       return tmp.tv_nsec > 0 ? 1 : (tmp.tv_nsec < 0 ? -1 : 0);
-}
diff --git a/libmultipath/time-util.h b/libmultipath/time-util.h
deleted file mode 100644 (file)
index 4a80ebd..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _TIME_UTIL_H_
-#define _TIME_UTIL_H_
-
-#include <pthread.h>
-
-struct timespec;
-
-void get_monotonic_time(struct timespec *res);
-void pthread_cond_init_mono(pthread_cond_t *cond);
-void normalize_timespec(struct timespec *ts);
-void timespecsub(const struct timespec *a, const struct timespec *b,
-                struct timespec *res);
-int timespeccmp(const struct timespec *a, const struct timespec *b);
-
-#endif /* _TIME_UTIL_H_ */
index 5793af944f964d63bbb68cd5c850844ce3783c44..57447ca058a64ef30afb649836bde506b96767dd 100644 (file)
@@ -373,7 +373,7 @@ uevent_filter(struct uevent *later, struct uevent_filter_state *st)
 
        list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
                /*
-                * filter unnessary earlier uevents
+                * filter unnecessary earlier uevents
                 * by the later uevent
                 */
                if (!list_empty(&earlier->merge_node)) {
diff --git a/libmultipath/util.c b/libmultipath/util.c
deleted file mode 100644 (file)
index ce5ea73..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
-#include <pthread.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <errno.h>
-#include <libudev.h>
-#include <mpath_persist.h>
-
-#include "util.h"
-#include "debug.h"
-#include "checkers.h"
-#include "vector.h"
-#include "structs.h"
-#include "config.h"
-#include "log.h"
-
-size_t
-strchop(char *str)
-{
-       size_t i;
-
-       for (i = strlen(str) - 1; i != (size_t) -1 && isspace(str[i]); i--) ;
-       str[++i] = '\0';
-       return i;
-}
-
-#ifndef __GLIBC__
-/*
- * glibc's non-destructive version of basename()
- * License: LGPL-2.1-or-later
- */
-static const char *__basename(const char *filename)
-{
-       char *p = strrchr(filename, '/');
-       return p ? p + 1 : filename;
-}
-#define basename(x) __basename(x)
-#endif
-
-int
-basenamecpy (const char *src, char *dst, size_t size)
-{
-       const char *p, *e;
-
-       if (!src || !dst || !strlen(src))
-               return 0;
-
-       p = basename(src);
-
-       for (e = p + strlen(p) - 1; e >= p && isspace(*e); --e) ;
-       if (e < p || (size_t)(e - p) > size - 2)
-               return 0;
-
-       strlcpy(dst, p, e - p + 2);
-       return strlen(dst);
-}
-
-int
-filepresent (const char *run) {
-       struct stat buf;
-
-       if(!stat(run, &buf))
-               return 1;
-       return 0;
-}
-
-char *get_next_string(char **temp, const char *split_char)
-{
-       char *token = NULL;
-       token = strsep(temp, split_char);
-       while (token != NULL && !strcmp(token, ""))
-               token = strsep(temp, split_char);
-       return token;
-}
-
-int
-get_word (const char *sentence, char **word)
-{
-       const char *p;
-       int len;
-       int skip = 0;
-
-       if (word)
-               *word = NULL;
-
-       while (*sentence ==  ' ') {
-               sentence++;
-               skip++;
-       }
-       if (*sentence == '\0')
-               return 0;
-
-       p = sentence;
-
-       while (*p !=  ' ' && *p != '\0')
-               p++;
-
-       len = (int) (p - sentence);
-
-       if (!word)
-               return skip + len;
-
-       *word = calloc(1, len + 1);
-
-       if (!*word) {
-               condlog(0, "get_word : oom");
-               return 0;
-       }
-       strncpy(*word, sentence, len);
-       strchop(*word);
-       condlog(5, "*word = %s, len = %i", *word, len);
-
-       if (*p == '\0')
-               return 0;
-
-       return skip + len;
-}
-
-size_t strlcpy(char * restrict dst, const char * restrict src, size_t size)
-{
-       size_t bytes = 0;
-       char ch;
-
-       while ((ch = *src++)) {
-               if (bytes + 1 < size)
-                       *dst++ = ch;
-               bytes++;
-       }
-
-       /* If size == 0 there is no space for a final null... */
-       if (size)
-               *dst = '\0';
-       return bytes;
-}
-
-size_t strlcat(char * restrict dst, const char * restrict src, size_t size)
-{
-       size_t bytes = 0;
-       char ch;
-
-       while (bytes < size && *dst) {
-               dst++;
-               bytes++;
-       }
-       if (bytes == size)
-               return (bytes + strlen(src));
-
-       while ((ch = *src++)) {
-               if (bytes + 1 < size)
-                       *dst++ = ch;
-               bytes++;
-       }
-
-       *dst = '\0';
-       return bytes;
-}
-
-int devt2devname(char *devname, int devname_len, const char *devt)
-{
-       struct udev_device *u_dev;
-       const char * dev_name;
-       int r;
-
-       if (!devname || !devname_len || !devt)
-               return 1;
-
-       u_dev = udev_device_new_from_devnum(udev, 'b', parse_devt(devt));
-       if (!u_dev) {
-               condlog(0, "\"%s\": invalid major/minor numbers, not found in sysfs", devt);
-               return 1;
-       }
-
-       dev_name = udev_device_get_sysname(u_dev);
-       if (!dev_name) {
-               udev_device_unref(u_dev);
-               return 1;
-       }
-       r = strlcpy(devname, dev_name, devname_len);
-       udev_device_unref(u_dev);
-
-       return !(r < devname_len);
-}
-
-/* This function returns a pointer inside of the supplied pathname string.
- * If is_path_device is true, it may also modify the supplied string */
-char *convert_dev(char *name, int is_path_device)
-{
-       char *ptr;
-
-       if (!name)
-               return NULL;
-       if (is_path_device) {
-               ptr = strstr(name, "cciss/");
-               if (ptr) {
-                       ptr += 5;
-                       *ptr = '!';
-               }
-       }
-       if (!strncmp(name, "/dev/", 5) && strlen(name) > 5)
-               ptr = name + 5;
-       else
-               ptr = name;
-       return ptr;
-}
-
-dev_t parse_devt(const char *dev_t)
-{
-       int maj, min;
-
-       if (sscanf(dev_t,"%d:%d", &maj, &min) != 2)
-               return 0;
-
-       return makedev(maj, min);
-}
-
-void
-setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
-{
-       int ret;
-
-       ret = pthread_attr_init(attr);
-       assert(ret == 0);
-       if (PTHREAD_STACK_MIN > 0 && stacksize < (size_t)PTHREAD_STACK_MIN)
-               stacksize = (size_t)PTHREAD_STACK_MIN;
-       ret = pthread_attr_setstacksize(attr, stacksize);
-       assert(ret == 0);
-       if (detached) {
-               ret = pthread_attr_setdetachstate(attr,
-                                                 PTHREAD_CREATE_DETACHED);
-               assert(ret == 0);
-       }
-}
-
-int systemd_service_enabled_in(const char *dev, const char *prefix)
-{
-       char path[PATH_SIZE], file[PATH_MAX], service[PATH_SIZE];
-       DIR *dirfd;
-       struct dirent *d;
-       int found = 0;
-
-       snprintf(service, PATH_SIZE, "multipathd.service");
-       snprintf(path, PATH_SIZE, "%s/systemd/system", prefix);
-       condlog(3, "%s: checking for %s in %s", dev, service, path);
-
-       dirfd = opendir(path);
-       if (dirfd == NULL)
-               return 0;
-
-       while ((d = readdir(dirfd)) != NULL) {
-               char *p;
-               struct stat stbuf;
-
-               if ((strcmp(d->d_name,".") == 0) ||
-                   (strcmp(d->d_name,"..") == 0))
-                       continue;
-
-               if (strlen(d->d_name) < 6)
-                       continue;
-
-               p = d->d_name + strlen(d->d_name) - 6;
-               if (strcmp(p, ".wants"))
-                       continue;
-               if (!safe_sprintf(file, "%s/%s/%s",
-                                 path, d->d_name, service)
-                   && stat(file, &stbuf) == 0) {
-                       condlog(3, "%s: found %s", dev, file);
-                       found++;
-                       break;
-               }
-       }
-       closedir(dirfd);
-
-       return found;
-}
-
-int systemd_service_enabled(const char *dev)
-{
-       int found = 0;
-
-       found = systemd_service_enabled_in(dev, "/etc");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/usr/lib");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/lib");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/run");
-       return found;
-}
-
-static int _linux_version_code;
-static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT;
-
-/* Returns current kernel version encoded as major*65536 + minor*256 + patch,
- * so, for example,  to check if the kernel is greater than 2.2.11:
- *
- *     if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
- *
- * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
- * Code copied from busybox (GPLv2 or later)
- */
-static void
-_set_linux_version_code(void)
-{
-       struct utsname name;
-       char *t;
-       int i, r;
-
-       uname(&name); /* never fails */
-       t = name.release;
-       r = 0;
-       for (i = 0; i < 3; i++) {
-               t = strtok(t, ".");
-               r = r * 256 + (t ? atoi(t) : 0);
-               t = NULL;
-       }
-       _linux_version_code = r;
-}
-
-int get_linux_version_code(void)
-{
-       pthread_once(&_lvc_initialized, _set_linux_version_code);
-       return _linux_version_code;
-}
-
-int parse_prkey(const char *ptr, uint64_t *prkey)
-{
-       if (!ptr)
-               return 1;
-       if (*ptr == '0')
-               ptr++;
-       if (*ptr == 'x' || *ptr == 'X')
-               ptr++;
-       if (*ptr == '\0' || strlen(ptr) > 16)
-               return 1;
-       if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
-               return 1;
-       if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
-               return 1;
-       return 0;
-}
-
-int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags)
-{
-       char *flagstr;
-
-       flagstr = strchr(ptr, ':');
-       *flags = 0;
-       if (flagstr) {
-               *flagstr++ = '\0';
-               if (strlen(flagstr) == 5 && strcmp(flagstr, "aptpl") == 0)
-                       *flags = MPATH_F_APTPL_MASK;
-       }
-       return parse_prkey(ptr, prkey);
-}
-
-int safe_write(int fd, const void *buf, size_t count)
-{
-       while (count > 0) {
-               ssize_t r = write(fd, buf, count);
-               if (r < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       return -errno;
-               }
-               count -= r;
-               buf = (const char *)buf + r;
-       }
-       return 0;
-}
-
-void set_max_fds(rlim_t max_fds)
-{
-       struct rlimit fd_limit;
-
-       if (!max_fds)
-               return;
-
-       if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
-               condlog(0, "can't get open fds limit: %s",
-                       strerror(errno));
-               fd_limit.rlim_cur = 0;
-               fd_limit.rlim_max = 0;
-       }
-       if (fd_limit.rlim_cur < max_fds) {
-               fd_limit.rlim_cur = max_fds;
-               if (fd_limit.rlim_max < max_fds)
-                       fd_limit.rlim_max = max_fds;
-               if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
-                       condlog(0, "can't set open fds limit to "
-                               "%lu/%lu : %s",
-                               (unsigned long)fd_limit.rlim_cur,
-                               (unsigned long)fd_limit.rlim_max,
-                               strerror(errno));
-               } else {
-                       condlog(3, "set open fds limit to %lu/%lu",
-                               (unsigned long)fd_limit.rlim_cur,
-                               (unsigned long)fd_limit.rlim_max);
-               }
-       }
-}
-
-void free_scandir_result(struct scandir_result *res)
-{
-       int i;
-
-       for (i = 0; i < res->n; i++)
-               free(res->di[i]);
-       free(res->di);
-}
-
-void close_fd(void *arg)
-{
-       close((long)arg);
-}
-
-void cleanup_free_ptr(void *arg)
-{
-       void **p = arg;
-
-       if (p && *p)
-               free(*p);
-}
-
-void cleanup_mutex(void *arg)
-{
-       pthread_mutex_unlock(arg);
-}
-
-struct bitfield *alloc_bitfield(unsigned int maxbit)
-{
-       unsigned int n;
-       struct bitfield *bf;
-
-       if (maxbit == 0) {
-               errno = EINVAL;
-               return NULL;
-       }
-
-       n = (maxbit - 1) / bits_per_slot + 1;
-       bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
-       if (bf)
-               bf->len = maxbit;
-       return bf;
-}
-
-void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len)
-{
-       condlog(0, "%s: bitfield overflow: %u >= %u", f, bit, len);
-}
-
-int should_exit(void)
-{
-       return 0;
-}
-
-void cleanup_charp(char **p)
-{
-       free(*p);
-}
-
-void cleanup_ucharp(unsigned char **p)
-{
-       free(*p);
-}
diff --git a/libmultipath/util.h b/libmultipath/util.h
deleted file mode 100644 (file)
index 5a44018..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#ifndef _UTIL_H
-#define _UTIL_H
-
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/types.h>
-/* for rlim_t */
-#include <sys/resource.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-size_t strchop(char *);
-int basenamecpy (const char *src, char *dst, size_t size);
-int filepresent (const char *run);
-char *get_next_string(char **temp, const char *split_char);
-int get_word (const char * sentence, char ** word);
-size_t strlcpy(char * restrict dst, const char * restrict src, size_t size);
-size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
-int devt2devname (char *, int, const char *);
-dev_t parse_devt(const char *dev_t);
-char *convert_dev(char *dev, int is_path_device);
-void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
-int systemd_service_enabled(const char *dev);
-int get_linux_version_code(void);
-int parse_prkey(const char *ptr, uint64_t *prkey);
-int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
-int safe_write(int fd, const void *buf, size_t count);
-void set_max_fds(rlim_t max_fds);
-int should_exit(void);
-
-#define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
-#define safe_sprintf(var, format, args...)     \
-       safe_snprintf(var, sizeof(var), format, ##args)
-
-#define safe_snprintf(var, size, format, args...)      \
-       ({                                                              \
-               size_t __size = size;                                   \
-               int __ret;                                              \
-                                                                       \
-               __ret = snprintf(var, __size, format, ##args);          \
-               __ret < 0 || (size_t)__ret >= __size;                   \
-       })
-
-#define pthread_cleanup_push_cast(f, arg)              \
-       pthread_cleanup_push(((void (*)(void *))&f), (arg))
-
-void close_fd(void *arg);
-void cleanup_free_ptr(void *arg);
-void cleanup_mutex(void *arg);
-
-struct scandir_result {
-       struct dirent **di;
-       int n;
-};
-void free_scandir_result(struct scandir_result *);
-
-#ifndef __GLIBC_PREREQ
-#define __GLIBC_PREREQ(x, y) 0
-#endif
-/*
- * ffsll() is also available on glibc < 2.27 if _GNU_SOURCE is defined.
- * But relying on that would require that every program using this header file
- * set _GNU_SOURCE during compilation, because otherwise the library and the
- * program would use different types for bitfield_t, causing errors.
- * That's too error prone, so if in doubt, use ffs().
- */
-#if __GLIBC_PREREQ(2, 27)
-typedef unsigned long long int bitfield_t;
-#define _ffs(x) ffsll(x)
-#else
-typedef unsigned int bitfield_t;
-#define _ffs(x) ffs(x)
-#endif
-#define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
-
-struct bitfield {
-       unsigned int len;
-       bitfield_t bits[];
-};
-
-#define STATIC_BITFIELD(name, length)                                  \
-       static struct {                                                 \
-               unsigned int len;                                       \
-               bitfield_t bits[((length) - 1) / bits_per_slot + 1];    \
-       } __static__ ## name = {                                        \
-               .len = (length),                                        \
-               .bits = { 0, },                                         \
-       }; \
-       struct bitfield *name = (struct bitfield *)& __static__ ## name
-
-struct 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)
-{
-       if (bit >= bf->len) {
-               log_bitfield_overflow(bit, bf->len);
-               return false;
-       }
-       return !!(bf->bits[bit / bits_per_slot] &
-                 (1ULL << (bit % bits_per_slot)));
-}
-
-static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
-{
-       if (bit >= bf->len) {
-               log_bitfield_overflow(bit, bf->len);
-               return;
-       }
-       bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
-}
-
-static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
-{
-       if (bit >= bf->len) {
-               log_bitfield_overflow(bit, bf->len);
-               return;
-       }
-       bf->bits[bit / bits_per_slot] &= ~(1ULL << (bit % bits_per_slot));
-}
-
-#define steal_ptr(x)                  \
-       ({                             \
-               void *___p = x;        \
-               x = NULL;              \
-               ___p;                  \
-       })
-
-void cleanup_charp(char **p);
-void cleanup_ucharp(unsigned char **p);
-#endif /* _UTIL_H */
diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c
deleted file mode 100644 (file)
index 2135476..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Original author : tridge@samba.org, January 2002
- *
- * Copyright (c) 2005 Christophe Varoqui
- * Copyright (c) 2005 Alasdair Kergon, Redhat
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <poll.h>
-#include <signal.h>
-#include <errno.h>
-#ifdef USE_SYSTEMD
-#include <systemd/sd-daemon.h>
-#endif
-#include "mpath_cmd.h"
-
-#include "uxsock.h"
-#include "debug.h"
-
-/*
- * Code is similar with mpath_recv_reply() with data size limitation
- * and debug-able malloc.
- * When limit == 0, it means no limit on data size, used for socket client
- * to receiving data from multipathd.
- */
-static int _recv_packet(int fd, char **buf, unsigned int timeout,
-                       ssize_t limit);
-
-/*
- * create a unix domain socket and start listening on it
- * return a file descriptor open on the socket
- */
-int ux_socket_listen(const char *name)
-{
-       int fd;
-       size_t len;
-#ifdef USE_SYSTEMD
-       int num;
-#endif
-       struct sockaddr_un addr;
-
-#ifdef USE_SYSTEMD
-       num = sd_listen_fds(0);
-       if (num > 1) {
-               condlog(3, "sd_listen_fds returned %d fds", num);
-               return -1;
-       } else if (num == 1) {
-               fd = SD_LISTEN_FDS_START + 0;
-               condlog(3, "using fd %d from sd_listen_fds", fd);
-               return fd;
-       }
-#endif
-       fd = socket(AF_LOCAL, SOCK_STREAM, 0);
-       if (fd == -1) {
-               condlog(3, "Couldn't create ux_socket, error %d", errno);
-               return -1;
-       }
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_LOCAL;
-       addr.sun_path[0] = '\0';
-       len = strlen(name) + 1;
-       if (len >= sizeof(addr.sun_path))
-               len = sizeof(addr.sun_path) - 1;
-       memcpy(&addr.sun_path[1], name, len);
-
-       len += sizeof(sa_family_t);
-       if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
-               condlog(3, "Couldn't bind to ux_socket, error %d", errno);
-               close(fd);
-               return -1;
-       }
-
-       if (listen(fd, 10) == -1) {
-               condlog(3, "Couldn't listen to ux_socket, error %d", errno);
-               close(fd);
-               return -1;
-       }
-       return fd;
-}
-
-/*
- * send a packet in length prefix format
- */
-int send_packet(int fd, const char *buf)
-{
-       if (mpath_send_cmd(fd, buf) < 0)
-               return -errno;
-       return 0;
-}
-
-static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit)
-{
-       int err = 0;
-       ssize_t len = 0;
-
-       *buf = NULL;
-       len = mpath_recv_reply_len(fd, timeout);
-       if (len == 0)
-               return len;
-       if (len < 0)
-               return -errno;
-       if ((limit > 0) && (len > limit))
-               return -EINVAL;
-       (*buf) = calloc(1, len);
-       if (!*buf)
-               return -ENOMEM;
-       err = mpath_recv_reply_data(fd, *buf, len, timeout);
-       if (err != 0) {
-               free(*buf);
-               (*buf) = NULL;
-               return -errno;
-       }
-       return err;
-}
-
-/*
- * receive a packet in length prefix format
- */
-int recv_packet(int fd, char **buf, unsigned int timeout)
-{
-       return _recv_packet(fd, buf, timeout, 0 /* no limit */);
-}
diff --git a/libmultipath/uxsock.h b/libmultipath/uxsock.h
deleted file mode 100644 (file)
index e3d28cf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/* some prototypes */
-int ux_socket_listen(const char *name);
-int send_packet(int fd, const char *buf);
-int recv_packet(int fd, char **buf, unsigned int timeout);
-
-#define _MAX_CMD_LEN           512
-
index ce1c7cbfdf3a7c41ebe5d78b1be776f5b3655c4e..731e6eff6190a442398875d07c7025354f377988 100644 (file)
@@ -23,7 +23,7 @@
  * already.
  * PATH_IS_VALID is returned by is_path_valid, when the path is
  * valid only if it hasn't been released to systemd already.
- * PATH_IS_MAYBE_VALID is returned when the the path would be valid
+ * PATH_IS_MAYBE_VALID is returned when the path would be valid
  * if other paths with the same wwid existed. It is up to the caller
  * to check for these other paths.
  */
diff --git a/libmultipath/vector.c b/libmultipath/vector.c
deleted file mode 100644 (file)
index e2d1ec9..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Part:        Vector structure manipulation.
- *
- * Version:     $Id: vector.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
- *
- * Author:      Alexandre Cassen, <acassen@linux-vs.org>
- *
- *              This program is distributed in the hope that it will be useful,
- *              but WITHOUT ANY WARRANTY; without even the implied warranty of
- *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *              See the GNU General Public License for more details.
- *
- *              This program is free software; you can redistribute it and/or
- *              modify it under the terms of the GNU General Public License
- *              as published by the Free Software Foundation; either version
- *              2 of the License, or (at your option) any later version.
- *
- * Copyright (c) 2002, 2003, 2004 Alexandre Cassen
- * Copyright (c) 2005 Christophe Varoqui
- */
-
-#include <stdlib.h>
-#include "vector.h"
-
-/*
- * Initialize vector struct.
- * allocated 'size' slot elements then return vector.
- */
-vector
-vector_alloc(void)
-{
-       vector v = (vector) calloc(1, sizeof (struct _vector));
-       return v;
-}
-
-/* allocated one slot */
-bool
-vector_alloc_slot(vector v)
-{
-       void *new_slot = NULL;
-       int new_allocated;
-       int i;
-
-       if (!v)
-               return false;
-
-       new_allocated = v->allocated + VECTOR_DEFAULT_SIZE;
-       new_slot = realloc(v->slot, sizeof (void *) * new_allocated);
-       if (!new_slot)
-               return false;
-
-       v->slot = new_slot;
-       for (i = v->allocated; i < new_allocated; i++)
-               v->slot[i] = NULL;
-
-       v->allocated = new_allocated;
-       return true;
-}
-
-int
-vector_move_up(vector v, int src, int dest)
-{
-       void *value;
-       int i;
-       if (dest == src)
-               return 0;
-       if (dest > src || src >= v->allocated)
-               return -1;
-       value = v->slot[src];
-       for (i = src - 1; i >= dest; i--)
-               v->slot[i + 1] = v->slot[i];
-       v->slot[dest] = value;
-       return 0;
-}
-
-void *
-vector_insert_slot(vector v, int slot, void *value)
-{
-       int i;
-
-       if (!vector_alloc_slot(v))
-               return NULL;
-
-       for (i = VECTOR_SIZE(v) - 2; i >= slot; i--)
-               v->slot[i + 1] = v->slot[i];
-
-       v->slot[slot] = value;
-
-       return v->slot[slot];
-}
-
-int
-find_slot(vector v, void * addr)
-{
-       int i;
-
-       if (!v)
-               return -1;
-
-       for (i = 0; i < VECTOR_SIZE(v); i++)
-               if (v->slot[i] == addr)
-                       return i;
-
-       return -1;
-}
-
-void
-vector_del_slot(vector v, int slot)
-{
-       int i;
-
-       if (!v || !v->allocated || slot < 0 || slot >= VECTOR_SIZE(v))
-               return;
-
-       for (i = slot + 1; i < VECTOR_SIZE(v); i++)
-               v->slot[i-1] = v->slot[i];
-
-       v->allocated -= VECTOR_DEFAULT_SIZE;
-
-       if (v->allocated <= 0) {
-               free(v->slot);
-               v->slot = NULL;
-               v->allocated = 0;
-       } else {
-               void *new_slot;
-
-               new_slot = realloc(v->slot, sizeof (void *) * v->allocated);
-               if (!new_slot)
-                       v->allocated += VECTOR_DEFAULT_SIZE;
-               else
-                       v->slot = new_slot;
-       }
-}
-
-void
-vector_repack(vector v)
-{
-       int i;
-
-       if (!v || !v->allocated)
-               return;
-
-       for (i = 0; i < VECTOR_SIZE(v); i++)
-               if (i > 0 && v->slot[i] == NULL)
-                       vector_del_slot(v, i--);
-}
-
-vector
-vector_reset(vector v)
-{
-       if (!v)
-               return NULL;
-
-       if (v->slot)
-               free(v->slot);
-
-       v->allocated = 0;
-       v->slot = NULL;
-       return v;
-}
-
-/* Free memory vector allocation */
-void
-vector_free(vector v)
-{
-       if (!vector_reset(v))
-               return;
-       free(v);
-}
-
-void
-free_strvec(vector strvec)
-{
-       int i;
-       char *str;
-
-       if (!strvec)
-               return;
-
-       vector_foreach_slot (strvec, str, i)
-               if (str)
-                       free(str);
-
-       vector_free(strvec);
-}
-
-/* Set a vector slot value */
-void
-vector_set_slot(vector v, void *value)
-{
-       unsigned int i;
-
-       if (!v)
-               return;
-
-       i = VECTOR_SIZE(v) - 1;
-       v->slot[i] = value;
-}
-
-int vector_find_or_add_slot(vector v, void *value)
-{
-       int n = find_slot(v, value);
-
-       if (n >= 0)
-               return n;
-       if (!vector_alloc_slot(v))
-               return -1;
-       vector_set_slot(v, value);
-       return VECTOR_SIZE(v) - 1;
-}
diff --git a/libmultipath/vector.h b/libmultipath/vector.h
deleted file mode 100644 (file)
index 2862dc2..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Soft:        Keepalived is a failover program for the LVS project
- *              <www.linuxvirtualserver.org>. It monitor & manipulate
- *              a loadbalanced server pool using multi-layer checks.
- *
- * Part:        vector.c include file.
- *
- * Version:     $Id: vector.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $
- *
- * Author:      Alexandre Cassen, <acassen@linux-vs.org>
- *
- *              This program is distributed in the hope that it will be useful,
- *              but WITHOUT ANY WARRANTY; without even the implied warranty of
- *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *              See the GNU General Public License for more details.
- *
- *              This program is free software; you can redistribute it and/or
- *              modify it under the terms of the GNU General Public License
- *              as published by the Free Software Foundation; either version
- *              2 of the License, or (at your option) any later version.
- */
-
-#ifndef _VECTOR_H
-#define _VECTOR_H
-
-#include <stdbool.h>
-
-/* vector definition */
-struct _vector {
-       int allocated;
-       void **slot;
-};
-typedef struct _vector *vector;
-
-#define VECTOR_DEFAULT_SIZE 1
-#define VECTOR_SIZE(V)   ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0)
-#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V) && (E) >= 0) ? (V)->slot[(E)] : NULL)
-#define VECTOR_LAST_SLOT(V)   (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
-
-#define vector_foreach_slot(v,p,i) \
-       for (i = 0; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
-#define vector_foreach_slot_after(v,p,i) \
-       for (; (v) && (int)i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++)
-#define vector_foreach_slot_backwards(v,p,i) \
-       for (i = VECTOR_SIZE(v) - 1; (int)i >= 0 && ((p) = (v)->slot[i]); i--)
-
-#define identity(x) (x)
-/*
- * Given a vector vec with elements of given type,
- * return a newly allocated vector with elements conv(e) for each element
- * e in vec. "conv" may be a macro or a function.
- * Use "identity" for a simple copy.
- */
-#define vector_convert(new, vec, type, conv)                           \
-       ({                                                              \
-               const struct _vector *__v = (vec);                      \
-               vector __t = (new);                                     \
-               type *__j;                                              \
-               int __i;                                                \
-                                                                       \
-               if (__t == NULL)                                        \
-                       __t = vector_alloc();                           \
-               if (__t != NULL) {                                      \
-                       vector_foreach_slot(__v, __j, __i) {            \
-                               if (!vector_alloc_slot(__t)) {  \
-                                       vector_free(__t);               \
-                                       __t = NULL;                     \
-                                       break;                          \
-                               }                                       \
-                               vector_set_slot(__t, conv(__j));        \
-                       }                                               \
-               }                                                       \
-               __t;                                                    \
-       })
-
-/* Prototypes */
-extern vector vector_alloc(void);
-extern bool vector_alloc_slot(vector v);
-vector vector_reset(vector v);
-extern void vector_free(vector v);
-#define vector_free_const(x) vector_free((vector)(long)(x))
-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 vector_find_or_add_slot(vector v, void *value);
-extern void vector_repack(vector v);
-extern void vector_dump(vector v);
-extern void dump_strvec(vector strvec);
-extern int vector_move_up(vector v, int src, int dest);
-#endif
index 7039941011e08b211eefc6065ae4c85e3098d2ef..fcc36d78d9f157681d8026f3d2e2b0bb2d81ba92 100644 (file)
@@ -20,9 +20,9 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000900
+#define VERSION_CODE 0x000901
 /* MMDDYY, in hex */
-#define DATE_CODE    0x050316
+#define DATE_CODE    0x090716
 
 #define PROG    "multipath-tools"
 
index 61d9c39e9e7a487412e83ea595624bbfcc58eb7d..89bb60caa96d5a29c33c3476895fa0d30f647f79 100644 (file)
@@ -90,7 +90,7 @@ int
 replace_wwids(vector mp)
 {
        int i, can_write;
-       long fd;
+       int fd = -1;
        struct multipath * mpp;
        size_t len;
        int ret = -1;
@@ -103,7 +103,7 @@ replace_wwids(vector mp)
        if (fd < 0)
                goto out;
 
-       pthread_cleanup_push(close_fd, (void*)fd);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
        if (!can_write) {
                condlog(0, "cannot replace wwids. wwids file is read-only");
                goto out_file;
@@ -196,7 +196,7 @@ do_remove_wwid(int fd, char *str) {
 
 int
 remove_wwid(char *wwid) {
-       long fd;
+       int fd = -1;
        int len, can_write;
        char *str;
        int ret = -1;
@@ -226,7 +226,7 @@ remove_wwid(char *wwid) {
                goto out;
        }
 
-       pthread_cleanup_push(close_fd, (void*)fd);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
        if (!can_write) {
                ret = -1;
                condlog(0, "cannot remove wwid. wwids file is read-only");
index 2e4d48363b2f8fc33ac8320ab881d99b4c2e32da..2219c86ae563167f49cc98595129c11d25e6132a 100644 (file)
@@ -1,11 +1,11 @@
 include ../Makefile.inc
 
-CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathpersistdir)
 CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
-       -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev
+       -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev
 
 EXEC = mpathpersist
 
index 4bdd55c2ee6b04aba4449b1e46aa99a1cda30c4f..894e8c943ca496289263c483db6298464405bda3 100644 (file)
@@ -480,7 +480,7 @@ static int handle_args(int argc, char * argv[], int nline)
        }
        if ((verbose > 2) && num_transportids)
        {
-               fprintf (stderr, "number of tranport-ids decoded from "
+               fprintf (stderr, "number of transport-ids decoded from "
                                "command line : %d\n", num_transportids);
        }
 
index bcb0453378943f7aaf29457f5da85329e63e7a0b..46b7553f60e7fc4c1c4545565862f9b0ff904d65 100644 (file)
@@ -3,11 +3,11 @@
 #
 include ../Makefile.inc
 
-CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir)
 CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \
-       -lpthread -ldevmapper -ldl -ludev
+LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathutildir) -lmpathutil \
+       -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ldl -ludev
 
 EXEC = multipath
 
index 034dd2f412253f761db1d9023054440ffd6ef4be..7b69a3ce08cf73d0ce54920c81415dd92f716623 100644 (file)
@@ -321,7 +321,7 @@ static int find_multipaths_check_timeout(const struct path *pp, long tmo,
        char path[PATH_MAX];
        struct timespec now, ftimes[2], tdiff;
        struct stat st;
-       long fd;
+       int fd;
        int r, retries = 0;
 
        clock_gettime(CLOCK_REALTIME, &now);
@@ -339,9 +339,8 @@ static int find_multipaths_check_timeout(const struct path *pp, long tmo,
 retry:
        fd = open(path, O_RDONLY);
        if (fd != -1) {
-               pthread_cleanup_push(close_fd, (void *)fd);
                r = fstat(fd, &st);
-               pthread_cleanup_pop(1);
+               close(fd);
 
        } else if (tmo > 0) {
                if (errno == ENOENT)
@@ -355,7 +354,6 @@ retry:
                        return FIND_MULTIPATHS_ERROR;
                };
 
-               pthread_cleanup_push(close_fd, (void *)fd);
                /*
                 * We just created the file. Set st_mtim to our desired
                 * expiry time.
@@ -369,7 +367,7 @@ retry:
                                path, strerror(errno));
                }
                r = fstat(fd, &st);
-               pthread_cleanup_pop(1);
+               close(fd);
        } else
                return FIND_MULTIPATHS_NEVER;
 
@@ -957,11 +955,6 @@ main (int argc, char *argv[])
                exit(RTVL_FAIL);
        }
 
-       if (check_alias_settings(conf)) {
-               fprintf(stderr, "fatal configuration error, aborting");
-               exit(RTVL_FAIL);
-       }
-
        if (optind < argc) {
                dev = calloc(1, FILE_NAME_SIZE);
 
@@ -988,20 +981,9 @@ main (int argc, char *argv[])
 
        libmp_udev_set_sync_support(1);
 
-       if (init_checkers()) {
-               condlog(0, "failed to initialize checkers");
-               goto out;
-       }
-       if (init_prio()) {
-               condlog(0, "failed to initialize prioritizers");
-               goto out;
-       }
-
        if ((cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) && enable_foreign)
                conf->enable_foreign = strdup("");
 
-       /* Failing here is non-fatal */
-       init_foreign(conf->enable_foreign);
        if (cmd == CMD_USABLE_PATHS) {
                r = check_usable_paths(conf, dev, dev_type) ?
                        RTVL_FAIL : RTVL_OK;
@@ -1036,6 +1018,23 @@ main (int argc, char *argv[])
                break;
        }
 
+       if (check_alias_settings(conf)) {
+               fprintf(stderr, "fatal configuration error, aborting");
+               exit(RTVL_FAIL);
+       }
+
+       if (init_checkers()) {
+               condlog(0, "failed to initialize checkers");
+               goto out;
+       }
+       if (init_prio()) {
+               condlog(0, "failed to initialize prioritizers");
+               goto out;
+       }
+
+       /* Failing here is non-fatal */
+       init_foreign(conf->enable_foreign);
+
        if (cmd == CMD_RESET_WWIDS) {
                struct multipath * mpp;
                int i;
index c2d34f18bf28a8b978f328614656c41d8a1a8d5e..acdd1ae6d8c74fca80db2b54bd85bca18d237d90 100644 (file)
@@ -306,7 +306,7 @@ generate the path priority. This prioritizer accepts the optional prio_arg
 .I emc
 (Hardware-dependent)
 Generate the path priority for DGC class arrays as CLARiiON CX/AX and
-EMC VNX and Unity families.
+EMC VNX families with Failover Mode 1 (Passive Not Ready(PNR)).
 .TP
 .I alua
 (Hardware-dependent)
@@ -315,12 +315,12 @@ accepts the optional prio_arg \fIexclusive_pref_bit\fR.
 .TP
 .I ontap
 (Hardware-dependent)
-Generate the path priority for NetApp ONTAP class and OEM arrays as IBM NSeries.
+Generate the path priority for NetApp ONTAP class, and rebranded arrays.
 .TP
 .I rdac
 (Hardware-dependent)
 Generate the path priority for LSI/Engenio/NetApp RDAC class as NetApp SANtricity
-E/EF Series, and OEM arrays from IBM DELL SGI STK and SUN.
+E/EF Series, and rebranded arrays.
 .TP
 .I hp_sw
 (Hardware-dependent)
@@ -455,7 +455,7 @@ precedence. See KNOWN ISSUES.
 (Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000.
 .TP
 .I queue_mode <mode>
-(Since kernel 4.8) Select the the queueing mode per multipath device.
+(Since kernel 4.8) Select the queueing mode per multipath device.
 <mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
 bio-based, request-based, and block-multiqueue (blk-mq) request-based,
 respectively.
@@ -496,7 +496,7 @@ Active/Standby mode exclusively.
 .I rdac
 (Hardware-dependent)
 Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
-Series, and OEM arrays from IBM DELL SGI STK and SUN.
+Series, and rebranded arrays.
 .TP
 .I directio
 Read the first sector with direct I/O. This checker could cause spurious path
@@ -935,7 +935,7 @@ This option is not supported any more. The value is ignored.
 .B san_path_err_threshold
 If set to a value greater than 0, multipathd will watch paths and check how many
 times a path has been failed due to errors.If the number of failures on a particular
-path is greater then the san_path_err_threshold, then the path will not reinstate
+path is greater than the san_path_err_threshold, then the path will not reinstate
 till san_path_err_recovery_time. These path failures should occur within a
 san_path_err_forget_rate checks, if not we will consider the path is good enough
 to reinstantate. See "Shaky paths detection" below.
@@ -949,7 +949,7 @@ The default is: \fBno\fR
 .B san_path_err_forget_rate
 If set to a value greater than 0, multipathd will check whether the path failures
 has exceeded  the san_path_err_threshold within this many checks i.e
-san_path_err_forget_rate . If so we will not reinstante the path till
+san_path_err_forget_rate . If so we will not reinstate the path till
 san_path_err_recovery_time. See "Shaky paths detection" below.
 .RS
 .TP
@@ -962,7 +962,7 @@ The default is: \fBno\fR
 If set to a value greater than 0, multipathd will make sure that when path failures
 has exceeded the san_path_err_threshold within san_path_err_forget_rate then the path
 will be placed in failed state for san_path_err_recovery_time duration.Once san_path_err_recovery_time
-has timeout  we will reinstante the failed path .
+has timeout  we will reinstate the failed path .
 san_path_err_recovery_time value should be in secs.
 See "Shaky paths detection" below.
 .RS
@@ -1000,7 +1000,7 @@ If the rate of IO error on a particular path is greater than the
 \fImarginal_path_err_rate_threshold\fR, then the path will not reinstate for
 \fImarginal_path_err_recheck_gap_time\fR seconds unless there is only one
 active path. After \fImarginal_path_err_recheck_gap_time\fR expires, the path
-will be requeueed for rechecking. If checking result is good enough, the
+will be requeued for rechecking. If checking result is good enough, the
 path will be reinstated. See "Shaky paths detection" below.
 .RS
 .TP
@@ -1031,7 +1031,7 @@ value, the failed path of  which the IO error rate is larger than
 \fImarginal_path_err_rate_threshold\fR will be kept in failed state for
 \fImarginal_path_err_recheck_gap_time\fR seconds. When
 \fImarginal_path_err_recheck_gap_time\fR seconds expires, the path will be
-requeueed for checking. If checking result is good enough, the path will be
+requeued for checking. If checking result is good enough, the path will be
 reinstated, or else it will keep failed. See "Shaky paths detection" below.
 .RS
 .TP
@@ -1379,7 +1379,7 @@ The protocol that a path is using can be viewed by running
 \fBmultipathd show paths format "%d %P"\fR
 .RE
 .LP
-For every device, these 5 blacklist criteria are evaluated in the the order
+For every device, these 5 blacklist criteria are evaluated in the order
 "property, dev\%node, device, protocol, wwid". If a device turns out to be
 blacklisted by any criterion, it's excluded from handling by multipathd, and
 the later criteria aren't evaluated any more. For each
@@ -1562,13 +1562,13 @@ The following hardware handler are implemented:
 .TP 12
 .I 1 emc
 (Hardware-dependent)
-Hardware handler for DGC class arrays as CLARiiON CX/AX and EMC VNX and Unity
-families.
+Hardware handler for DGC class arrays as CLARiiON CX/AX and EMC VNX families
+with Failover Mode 1 (Passive Not Ready(PNR)).
 .TP
 .I 1 rdac
 (Hardware-dependent)
 Hardware handler for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
-Series, and OEM arrays from IBM DELL SGI STK and SUN.
+Series, and rebranded arrays.
 .TP
 .I 1 hp_sw
 (Hardware-dependent)
index 9df11a95d3f4f382092ba5a1b3918550a95c0e2d..f993d99672f2b61761d376b32bd4a87bba3e7921 100644 (file)
@@ -71,7 +71,7 @@ ENV{.SAVED_FM_WAIT_UNTIL}=="?*", GOTO="pretend_mpath"
 #
 # We must trigger an "add" event because LVM2 will only act on those.
 
-RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Wants=multipathd.service --property After=multipathd.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /usr/bin/udevadm trigger --action=add $sys$devpath"
+RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property Conflicts=initrd-cleanup.service --timer-property Before=initrd-cleanup.service --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Conflicts=initrd-cleanup.service --property Before=initrd-cleanup.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /usr/bin/udevadm trigger --action=add $sys$devpath"
 
 LABEL="pretend_mpath"
 ENV{DM_MULTIPATH_DEVICE_PATH}="1"
index c937cd55a1387e007bf4193e068aebf759bd9080..3ce9465ec5f3a8dc0a4dc6941222cd19707c9fa9 100644 (file)
@@ -15,21 +15,37 @@ endif
 #CPPFLAGS += -D_DEBUG_
 #CPPFLAGS += -DLOGDBG
 
-CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) -I$(thirdpartydir) \
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathpersistdir) -I$(mpathcmddir) -I$(thirdpartydir) \
        $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
-               awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
+               awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }') \
+       -DBINDIR='"$(bindir)"'
 CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
+
+CLI_LIBDEPS :=  -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
-          -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
-          -ldevmapper -lreadline
+          -ldevmapper $(CLI_LIBDEPS)
+
+
+ifeq ($(READLINE),libedit)
+RL_CPPFLAGS = -DUSE_LIBEDIT
+RL_LIBDEPS += -ledit
+endif
+ifeq ($(READLINE),libreadline)
+RL_CPPFLAGS += -DUSE_LIBREADLINE
+RL_LIBDEPS += -lreadline
+# See comment in uxclnt.c
+ifeq ($(shell sed -En 's/.*\<Function\s*\*rl_completion_entry_function;.*/yes/p' /usr/include/editline/readline.h),yes)
+RL_CPPFLAGS += -DBROKEN_RL_COMPLETION_FUNC
+endif
+endif
 
 ifdef SYSTEMD
        CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
        ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
-               LIBDEPS += -lsystemd
+               CLI_LIBDEPS += -lsystemd
        else
-               LIBDEPS += -lsystemd-daemon
+               CLI_LIBDEPS += -lsystemd-daemon
        endif
 endif
 ifeq ($(ENABLE_DMEVENTS_POLL),0)
@@ -39,6 +55,8 @@ endif
 OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
        dmevents.o init_unwinder.o
 
+CLI_OBJS = multipathc.o cli.o
+
 ifeq ($(FPIN_SUPPORT),1)
 OBJS += fpin_handlers.o
 endif
@@ -46,18 +64,26 @@ endif
 
 
 EXEC = multipathd
+CLI = multipathc
 
-all : $(EXEC)
+all : $(EXEC) $(CLI)
 
 $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
        $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS)
 
+multipathc.o:  multipathc.c
+       $(CC) $(CPPFLAGS) $(RL_CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+
+$(CLI):  $(CLI_OBJS)
+       $(CC) $(CFLAGS) $(CLI_OBJS) $(LDFLAGS) -o $@ $(CLI_LIBDEPS) $(RL_LIBDEPS)
+
 cli_handlers.o:        cli_handlers.c
        $(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
 
 install:
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
        $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)
+       $(INSTALL_PROGRAM) -m 755 $(CLI) $(DESTDIR)$(bindir)
 ifdef SYSTEMD
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(unitdir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).service $(DESTDIR)$(unitdir)
@@ -65,15 +91,17 @@ ifdef SYSTEMD
 endif
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
        $(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
+       $(INSTALL_PROGRAM) -m 644 $(CLI).8 $(DESTDIR)$(man8dir)
 
 uninstall:
-       $(RM) $(DESTDIR)$(bindir)/$(EXEC)
+       $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(DESTDIR)$(bindir)/$(CLI)
        $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
+       $(RM) $(DESTDIR)$(man8dir)/$(CLI).8
        $(RM) $(DESTDIR)$(unitdir)/$(EXEC).service
        $(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket
 
 clean: dep_clean
-       $(RM) core *.o $(EXEC)
+       $(RM) core *.o $(EXEC) $(CLI)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff --git a/multipathd/callbacks.c b/multipathd/callbacks.c
new file mode 100644 (file)
index 0000000..0bd76b7
--- /dev/null
@@ -0,0 +1,60 @@
+void init_handler_callbacks(void)
+{
+       set_handler_callback(LIST+PATHS, HANDLER(cli_list_paths));
+       set_handler_callback(LIST+PATHS+FMT, HANDLER(cli_list_paths_fmt));
+       set_handler_callback(LIST+PATHS+RAW+FMT, HANDLER(cli_list_paths_raw));
+       set_handler_callback(LIST+PATH, HANDLER(cli_list_path));
+       set_handler_callback(LIST+MAPS, HANDLER(cli_list_maps));
+       set_handler_callback(LIST+STATUS, HANDLER(cli_list_status));
+       set_unlocked_handler_callback(LIST+DAEMON, HANDLER(cli_list_daemon));
+       set_handler_callback(LIST+MAPS+STATUS, HANDLER(cli_list_maps_status));
+       set_handler_callback(LIST+MAPS+STATS, HANDLER(cli_list_maps_stats));
+       set_handler_callback(LIST+MAPS+FMT, HANDLER(cli_list_maps_fmt));
+       set_handler_callback(LIST+MAPS+RAW+FMT, HANDLER(cli_list_maps_raw));
+       set_handler_callback(LIST+MAPS+TOPOLOGY, HANDLER(cli_list_maps_topology));
+       set_handler_callback(LIST+TOPOLOGY, HANDLER(cli_list_maps_topology));
+       set_handler_callback(LIST+MAPS+JSON, HANDLER(cli_list_maps_json));
+       set_handler_callback(LIST+MAP+TOPOLOGY, HANDLER(cli_list_map_topology));
+       set_handler_callback(LIST+MAP+FMT, HANDLER(cli_list_map_fmt));
+       set_handler_callback(LIST+MAP+RAW+FMT, HANDLER(cli_list_map_fmt));
+       set_handler_callback(LIST+MAP+JSON, HANDLER(cli_list_map_json));
+       set_handler_callback(LIST+CONFIG+LOCAL, HANDLER(cli_list_config_local));
+       set_handler_callback(LIST+CONFIG, HANDLER(cli_list_config));
+       set_handler_callback(LIST+BLACKLIST, HANDLER(cli_list_blacklist));
+       set_handler_callback(LIST+DEVICES, HANDLER(cli_list_devices));
+       set_handler_callback(LIST+WILDCARDS, HANDLER(cli_list_wildcards));
+       set_handler_callback(RESET+MAPS+STATS, HANDLER(cli_reset_maps_stats));
+       set_handler_callback(RESET+MAP+STATS, HANDLER(cli_reset_map_stats));
+       set_handler_callback(ADD+PATH, HANDLER(cli_add_path));
+       set_handler_callback(DEL+PATH, HANDLER(cli_del_path));
+       set_handler_callback(ADD+MAP, HANDLER(cli_add_map));
+       set_handler_callback(DEL+MAP, HANDLER(cli_del_map));
+       set_handler_callback(DEL+MAPS, HANDLER(cli_del_maps));
+       set_handler_callback(SWITCH+MAP+GROUP, HANDLER(cli_switch_group));
+       set_unlocked_handler_callback(RECONFIGURE, HANDLER(cli_reconfigure));
+       set_unlocked_handler_callback(RECONFIGURE+ALL, HANDLER(cli_reconfigure_all));
+       set_handler_callback(SUSPEND+MAP, HANDLER(cli_suspend));
+       set_handler_callback(RESUME+MAP, HANDLER(cli_resume));
+       set_handler_callback(RESIZE+MAP, HANDLER(cli_resize));
+       set_handler_callback(RELOAD+MAP, HANDLER(cli_reload));
+       set_handler_callback(RESET+MAP, HANDLER(cli_reassign));
+       set_handler_callback(REINSTATE+PATH, HANDLER(cli_reinstate));
+       set_handler_callback(FAIL+PATH, HANDLER(cli_fail));
+       set_handler_callback(DISABLEQ+MAP, HANDLER(cli_disable_queueing));
+       set_handler_callback(RESTOREQ+MAP, HANDLER(cli_restore_queueing));
+       set_handler_callback(DISABLEQ+MAPS, HANDLER(cli_disable_all_queueing));
+       set_handler_callback(RESTOREQ+MAPS, HANDLER(cli_restore_all_queueing));
+       set_unlocked_handler_callback(QUIT, HANDLER(cli_quit));
+       set_unlocked_handler_callback(SHUTDOWN, HANDLER(cli_shutdown));
+       set_handler_callback(GETPRSTATUS+MAP, HANDLER(cli_getprstatus));
+       set_handler_callback(SETPRSTATUS+MAP, HANDLER(cli_setprstatus));
+       set_handler_callback(UNSETPRSTATUS+MAP, HANDLER(cli_unsetprstatus));
+       set_handler_callback(FORCEQ+DAEMON, HANDLER(cli_force_no_daemon_q));
+       set_handler_callback(RESTOREQ+DAEMON, HANDLER(cli_restore_no_daemon_q));
+       set_handler_callback(GETPRKEY+MAP, HANDLER(cli_getprkey));
+       set_handler_callback(SETPRKEY+MAP+KEY, HANDLER(cli_setprkey));
+       set_handler_callback(UNSETPRKEY+MAP, HANDLER(cli_unsetprkey));
+       set_handler_callback(SETMARGINAL+PATH, HANDLER(cli_set_marginal));
+       set_handler_callback(UNSETMARGINAL+PATH, HANDLER(cli_unset_marginal));
+       set_handler_callback(UNSETMARGINAL+MAP, HANDLER(cli_unset_all_marginal));
+}
index b2ee9a99f08847a8c4661f8b727a3c3b36c07ebe..5d25ddb94fa6ba16170d22bde2922f65792b5c79 100644 (file)
 #include "parser.h"
 #include "util.h"
 #include "version.h"
-#include <readline/readline.h>
 
 #include "mpath_cmd.h"
 #include "cli.h"
+#include "cli_handlers.h"
 #include "debug.h"
 #include "strbuf.h"
 
 static vector keys;
 static vector handlers;
 
+vector get_keys(void)
+{
+       return keys;
+}
+
+vector get_handlers(void)
+{
+       return handlers;
+}
+
 static struct key *
 alloc_key (void)
 {
@@ -220,8 +230,7 @@ load_keys (void)
        return 0;
 }
 
-static struct key *
-find_key (const char * str)
+struct key *find_key (const char * str)
 {
        int i;
        int len, klen;
@@ -318,8 +327,7 @@ out:
        return r;
 }
 
-static uint64_t
-fingerprint(const struct _vector *vec)
+uint64_t fingerprint(const struct _vector *vec)
 {
        int i;
        uint64_t fp = 0;
@@ -444,6 +452,7 @@ cli_init (void) {
        if (alloc_handlers())
                return 1;
 
+       init_handler_callbacks();
        return 0;
 }
 
@@ -453,109 +462,3 @@ void cli_exit(void)
        free_keys(keys);
        keys = NULL;
 }
-
-static int
-key_match_fingerprint (struct key * kw, uint64_t fp)
-{
-       if (!fp)
-               return 0;
-
-       return ((fp & kw->code) == kw->code);
-}
-
-/*
- * This is the readline completion handler
- */
-char *
-key_generator (const char * str, int state)
-{
-       static int index, len, has_param;
-       static uint64_t rlfp;
-       struct key * kw;
-       int i;
-       struct handler *h;
-       vector v = NULL;
-
-       if (!state) {
-               index = 0;
-               has_param = 0;
-               rlfp = 0;
-               len = strlen(str);
-               int r = get_cmdvec(rl_line_buffer, &v);
-               /*
-                * If a word completion is in progress, we don't want
-                * to take an exact keyword match in the fingerprint.
-                * For ex "show map[tab]" would validate "map" and discard
-                * "maps" as a valid candidate.
-                */
-               if (v && len)
-                       vector_del_slot(v, VECTOR_SIZE(v) - 1);
-               /*
-                * Clean up the mess if we dropped the last slot of a 1-slot
-                * vector
-                */
-               if (v && !VECTOR_SIZE(v)) {
-                       vector_free(v);
-                       v = NULL;
-               }
-               /*
-                * If last keyword takes a param, don't even try to guess
-                */
-               if (r == EINVAL) {
-                       has_param = 1;
-                       return (strdup("(value)"));
-               }
-               /*
-                * Compute a command fingerprint to find out possible completions.
-                * Once done, the vector is useless. Free it.
-                */
-               if (v) {
-                       rlfp = fingerprint(v);
-                       free_keys(v);
-               }
-       }
-       /*
-        * No more completions for parameter placeholder.
-        * Brave souls might try to add parameter completion by walking paths and
-        * multipaths vectors.
-        */
-       if (has_param)
-               return ((char *)NULL);
-       /*
-        * Loop through keywords for completion candidates
-        */
-       vector_foreach_slot_after (keys, kw, index) {
-               if (!strncmp(kw->str, str, len)) {
-                       /*
-                        * Discard keywords already in the command line
-                        */
-                       if (key_match_fingerprint(kw, rlfp)) {
-                               struct key * curkw = find_key(str);
-                               if (!curkw || (curkw != kw))
-                                       continue;
-                       }
-                       /*
-                        * Discard keywords making syntax errors.
-                        *
-                        * nfp is the candidate fingerprint we try to
-                        * validate against all known command fingerprints.
-                        */
-                       uint64_t nfp = rlfp | kw->code;
-                       vector_foreach_slot(handlers, h, i) {
-                               if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
-                                       /*
-                                        * At least one full command is
-                                        * possible with this keyword :
-                                        * Consider it validated
-                                        */
-                                       index++;
-                                       return (strdup(kw->str));
-                               }
-                       }
-               }
-       }
-       /*
-        * No more candidates
-        */
-       return ((char *)NULL);
-}
index a6082ac0d17ca38262895b2fe2d6880fa5faf0a0..cb5bbe2000dfafdfb7e5c09e79e7f5914b69e7ef 100644 (file)
@@ -151,6 +151,9 @@ void free_keys (vector vec);
 void free_handlers (void);
 int cli_init (void);
 void cli_exit(void);
-char * key_generator (const char * str, int state);
+uint64_t fingerprint(const struct _vector *vec);
+vector get_keys(void);
+vector get_handlers(void);
+struct key *find_key (const char * str);
 
 #endif /* _CLI_H_ */
index d79cdd7c895785a102fae857ff232e5c9c466004..5b8f647bd49c4ec1db744477a2dbbae46a57fcfb 100644 (file)
@@ -420,7 +420,7 @@ cli_list_map_fmt (void *v, struct strbuf *reply, void *data)
 
        if ((width = alloc_multipath_layout()) == NULL)
                return 1;
-       get_multipath_layout(vecs->pathvec, 1, width);
+       get_multipath_layout(vecs->mpvec, 1, width);
        param = convert_dev(param, 0);
        mpp = find_mp_by_str(vecs->mpvec, param);
        if (!mpp)
@@ -688,7 +688,7 @@ cli_add_map (void * v, struct strbuf *reply, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, MAP);
        int major = -1, minor = -1;
-       char dev_path[PATH_SIZE];
+       char dev_path[FILE_NAME_SIZE];
        char *refwwid, *alias = NULL;
        int rc, count = 0;
        struct config *conf;
@@ -1519,63 +1519,5 @@ static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
        return reload_and_sync_map(mpp, vecs, 0);
 }
 
-void init_handler_callbacks(void)
-{
-       set_handler_callback(LIST+PATHS, cli_list_paths);
-       set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
-       set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
-       set_handler_callback(LIST+PATH, cli_list_path);
-       set_handler_callback(LIST+MAPS, cli_list_maps);
-       set_handler_callback(LIST+STATUS, cli_list_status);
-       set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon);
-       set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
-       set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
-       set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
-       set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
-       set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
-       set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
-       set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json);
-       set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
-       set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt);
-       set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt);
-       set_handler_callback(LIST+MAP+JSON, cli_list_map_json);
-       set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local);
-       set_handler_callback(LIST+CONFIG, cli_list_config);
-       set_handler_callback(LIST+BLACKLIST, cli_list_blacklist);
-       set_handler_callback(LIST+DEVICES, cli_list_devices);
-       set_handler_callback(LIST+WILDCARDS, cli_list_wildcards);
-       set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats);
-       set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats);
-       set_handler_callback(ADD+PATH, cli_add_path);
-       set_handler_callback(DEL+PATH, cli_del_path);
-       set_handler_callback(ADD+MAP, cli_add_map);
-       set_handler_callback(DEL+MAP, cli_del_map);
-       set_handler_callback(DEL+MAPS, cli_del_maps);
-       set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
-       set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
-       set_unlocked_handler_callback(RECONFIGURE+ALL, cli_reconfigure_all);
-       set_handler_callback(SUSPEND+MAP, cli_suspend);
-       set_handler_callback(RESUME+MAP, cli_resume);
-       set_handler_callback(RESIZE+MAP, cli_resize);
-       set_handler_callback(RELOAD+MAP, cli_reload);
-       set_handler_callback(RESET+MAP, cli_reassign);
-       set_handler_callback(REINSTATE+PATH, cli_reinstate);
-       set_handler_callback(FAIL+PATH, cli_fail);
-       set_handler_callback(DISABLEQ+MAP, cli_disable_queueing);
-       set_handler_callback(RESTOREQ+MAP, cli_restore_queueing);
-       set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing);
-       set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing);
-       set_unlocked_handler_callback(QUIT, cli_quit);
-       set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
-       set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus);
-       set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus);
-       set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
-       set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
-       set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
-       set_handler_callback(GETPRKEY+MAP, cli_getprkey);
-       set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
-       set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
-       set_handler_callback(SETMARGINAL+PATH, cli_set_marginal);
-       set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal);
-       set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal);
-}
+#define HANDLER(x) x
+#include "callbacks.c"
index 384ae31818809e5a8d971eff73954ed8ef985e69..03b2b9adb81e9e4a36cc48776b50786c82a187cb 100644 (file)
@@ -172,8 +172,15 @@ fpin_els_add_li_frame(struct fc_nl_event *fc_event)
 /*Sets the rport port_state to marginal*/
 static void fpin_set_rport_marginal(struct udev_device *rport_dev)
 {
-       sysfs_attr_set_value(rport_dev, "port_state",
-                               "Marginal", strlen("Marginal"));
+       static const char marginal[] = "Marginal";
+       ssize_t ret;
+
+       ret = sysfs_attr_set_value(rport_dev, "port_state",
+                                  marginal, sizeof(marginal) - 1);
+       if (ret != sizeof(marginal) - 1)
+               log_sysfs_attr_set_value(2, ret,
+                                        "%s: failed to set port_state to marginal",
+                                        udev_device_get_syspath(rport_dev));
 }
 
 /*Add the marginal devices info into the list*/
@@ -481,7 +488,7 @@ static void receiver_cleanup_list(__attribute__((unused)) void *arg)
 void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
 {
        int ret;
-       long fd;
+       int fd = -1;
        uint32_t els_cmd;
        struct fc_nl_event *fc_event = NULL;
        struct sockaddr_nl fc_local;
@@ -492,13 +499,14 @@ void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
        rcu_register_thread();
 
        pthread_cleanup_push(receiver_cleanup_list, NULL);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
+
        fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT);
        if (fd < 0) {
-               condlog(0, "fc socket error %ld", fd);
-               return NULL;
+               condlog(0, "fc socket error %d", fd);
+               goto out;
        }
 
-       pthread_cleanup_push(close_fd, (void *)fd);
        memset(&fc_local, 0, sizeof(fc_local));
        fc_local.nl_family = AF_NETLINK;
        fc_local.nl_groups = ~0;
index 2f2b9d4cb4dc0f784670374c9cea93f38144a6eb..ba52d393f20102e19d3bb49908e66c7a64906844 100644 (file)
@@ -561,6 +561,30 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
        return 0;
 }
 
+static bool
+flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
+       char alias[WWID_SIZE];
+
+       /*
+        * flush_map will fail if the device is open
+        */
+       strlcpy(alias, mpp->alias, WWID_SIZE);
+       if (mpp->flush_on_last_del == FLUSH_ENABLED) {
+               condlog(2, "%s Last path deleted, disabling queueing",
+                       mpp->alias);
+               mpp->retry_tick = 0;
+               mpp->no_path_retry = NO_PATH_RETRY_FAIL;
+               mpp->disable_queueing = 1;
+               mpp->stat_map_failures++;
+               dm_queue_if_no_path(mpp->alias, 0);
+       }
+       if (!flush_map(mpp, vecs, 1)) {
+               condlog(2, "%s: removed map after removing all paths", alias);
+               return true;
+       }
+       return false;
+}
+
 static int
 update_map (struct multipath *mpp, struct vectors *vecs, int new_map)
 {
@@ -578,12 +602,16 @@ retry:
                goto fail;
        }
        verify_paths(mpp);
+       if (VECTOR_SIZE(mpp->paths) == 0 &&
+           flush_map_nopaths(mpp, vecs))
+               return 1;
+
        mpp->action = ACT_RELOAD;
 
        if (mpp->prflag) {
                vector_foreach_slot(mpp->paths, pp, i) {
                        if ((pp->state == PATH_UP)  || (pp->state == PATH_GHOST)) {
-                               /* persistent reseravtion check*/
+                               /* persistent reservation check*/
                                mpath_pr_event_handle(pp);
                        }
                }
@@ -734,10 +762,6 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
         * the spurious uevent we may generate with the dm_flush_map call below
         */
        if (r) {
-               /*
-                * May not really be an error -- if the map was already flushed
-                * from the device mapper by dmsetup(8) for instance.
-                */
                if (r == 1)
                        condlog(0, "%s: can't flush", mpp->alias);
                else {
@@ -911,26 +935,39 @@ rescan_path(struct udev_device *ud)
 {
        ud = udev_device_get_parent_with_subsystem_devtype(ud, "scsi",
                                                           "scsi_device");
-       if (ud)
-               sysfs_attr_set_value(ud, "rescan", "1", strlen("1"));
+       if (ud) {
+               ssize_t ret =
+                       sysfs_attr_set_value(ud, "rescan", "1", strlen("1"));
+               if (ret != strlen("1"))
+                       log_sysfs_attr_set_value(1, ret,
+                                                "%s: failed to trigger rescan",
+                                                udev_device_get_syspath(ud));
+       }
 }
 
 void
 handle_path_wwid_change(struct path *pp, struct vectors *vecs)
 {
        struct udev_device *udd;
+       static const char add[] = "add";
+       ssize_t ret;
+       char dev[FILE_NAME_SIZE];
 
        if (!pp || !pp->udev)
                return;
 
+       strlcpy(dev, pp->dev, sizeof(dev));
        udd = udev_device_ref(pp->udev);
        if (!(ev_remove_path(pp, vecs, 1) & REMOVE_PATH_SUCCESS) && pp->mpp) {
                pp->dmstate = PSTATE_FAILED;
                dm_fail_path(pp->mpp->alias, pp->dev_t);
        }
        rescan_path(udd);
-       sysfs_attr_set_value(udd, "uevent", "add", strlen("add"));
+       ret = sysfs_attr_set_value(udd, "uevent", add, sizeof(add) - 1);
        udev_device_unref(udd);
+       if (ret != sizeof(add) - 1)
+               log_sysfs_attr_set_value(1, ret,
+                                        "%s: failed to trigger add event", dev);
 }
 
 bool
@@ -1126,7 +1163,7 @@ sysfs_get_ro (struct path *pp)
        if (!pp->udev)
                return -1;
 
-       if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) {
+       if (!sysfs_attr_get_value_ok(pp->udev, "ro", buff, sizeof(buff))) {
                condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev);
                return -1;
        }
@@ -1351,34 +1388,12 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                        vector_del_slot(mpp->paths, i);
 
                /*
-                * remove the map IF removing the last path
+                * remove the map IF removing the last path. If
+                * flush_map_nopaths succeeds, the path has been removed.
                 */
-               if (VECTOR_SIZE(mpp->paths) == 0) {
-                       char alias[WWID_SIZE];
-
-                       /*
-                        * flush_map will fail if the device is open
-                        */
-                       strlcpy(alias, mpp->alias, WWID_SIZE);
-                       if (mpp->flush_on_last_del == FLUSH_ENABLED) {
-                               condlog(2, "%s Last path deleted, disabling queueing", mpp->alias);
-                               mpp->retry_tick = 0;
-                               mpp->no_path_retry = NO_PATH_RETRY_FAIL;
-                               mpp->disable_queueing = 1;
-                               mpp->stat_map_failures++;
-                               dm_queue_if_no_path(mpp->alias, 0);
-                       }
-                       if (!flush_map(mpp, vecs, 1)) {
-                               condlog(2, "%s: removed map after"
-                                       " removing all paths",
-                                       alias);
-                               /* flush_map() has freed the path */
-                               goto out;
-                       }
-                       /*
-                        * Not an error, continue
-                        */
-               }
+               if (VECTOR_SIZE(mpp->paths) == 0 &&
+                   flush_map_nopaths(mpp, vecs))
+                       goto out;
 
                if (setup_map(mpp, &params, vecs)) {
                        condlog(0, "%s: failed to setup map for"
@@ -1503,7 +1518,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                condlog(3, "%s: error in change_foreign", __func__);
                break;
        default:
-               condlog(1, "%s: return code %d of change_forein is unsupported",
+               condlog(1, "%s: return code %d of change_foreign is unsupported",
                        __func__, rc);
                break;
        }
@@ -1782,7 +1797,6 @@ uxlsnrloop (void * ap)
        /* Tell main thread that thread has started */
        post_config_state(DAEMON_CONFIGURE);
 
-       init_handler_callbacks();
        umask(077);
 
        /*
@@ -1954,7 +1968,7 @@ ghost_delay_tick(struct vectors *vecs)
 }
 
 static void
-defered_failback_tick (vector mpvec)
+deferred_failback_tick (vector mpvec)
 {
        struct multipath * mpp;
        unsigned int i;
@@ -2003,9 +2017,14 @@ partial_retrigger_tick(vector pathvec)
                    --pp->partial_retrigger_delay == 0) {
                        const char *msg = udev_device_get_is_initialized(pp->udev) ?
                                          "change" : "add";
-
-                       sysfs_attr_set_value(pp->udev, "uevent", msg,
-                                            strlen(msg));
+                       ssize_t len = strlen(msg);
+                       ssize_t ret = sysfs_attr_set_value(pp->udev, "uevent", msg,
+                                                          len);
+
+                       if (len != ret)
+                               log_sysfs_attr_set_value(2, ret,
+                                       "%s: failed to trigger %s event",
+                                       pp->dev, msg);
                }
        }
 }
@@ -2169,7 +2188,7 @@ static int check_path_reinstate_state(struct path * pp) {
        get_monotonic_time(&curr_time);
        /* when path failures has exceeded the san_path_err_threshold
         * place the path in delayed state till san_path_err_recovery_time
-        * so that the cutomer can rectify the issue within this time. After
+        * so that the customer can rectify the issue within this time. After
         * the completion of san_path_err_recovery_time it should
         * automatically reinstate the path
         * (note: we know that san_path_err_threshold > 0 here).
@@ -2245,12 +2264,19 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
 
        if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV) {
                if (pp->retriggers < retrigger_tries) {
+                       static const char change[] = "change";
+                       ssize_t ret;
+
                        condlog(2, "%s: triggering change event to reinitialize",
                                pp->dev);
                        pp->initialized = INIT_REQUESTED_UDEV;
                        pp->retriggers++;
-                       sysfs_attr_set_value(pp->udev, "uevent", "change",
-                                            strlen("change"));
+                       ret = sysfs_attr_set_value(pp->udev, "uevent", change,
+                                                  sizeof(change) - 1);
+                       if (ret != sizeof(change) - 1)
+                               log_sysfs_attr_set_value(1, ret,
+                                                        "%s: failed to trigger change event",
+                                                        pp->dev);
                        return 0;
                } else {
                        condlog(1, "%s: not initialized after %d udev retriggers",
@@ -2548,6 +2574,11 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        }
        return 1;
 }
+enum checker_state {
+       CHECKER_STARTING,
+       CHECKER_RUNNING,
+       CHECKER_FINISHED,
+};
 
 static void *
 checkerloop (void *ap)
@@ -2555,7 +2586,6 @@ checkerloop (void *ap)
        struct vectors *vecs;
        struct path *pp;
        int count = 0;
-       unsigned int i;
        struct timespec last_time;
        struct config *conf;
        int foreign_tick = 0;
@@ -2581,49 +2611,91 @@ checkerloop (void *ap)
 
        while (1) {
                struct timespec diff_time, start_time, end_time;
-               int num_paths = 0, strict_timing, rc = 0;
+               int num_paths = 0, strict_timing, rc = 0, i = 0;
                unsigned int ticks = 0;
+               enum checker_state checker_state = CHECKER_STARTING;
 
                if (set_config_state(DAEMON_RUNNING) != DAEMON_RUNNING)
                        /* daemon shutdown */
                        break;
 
                get_monotonic_time(&start_time);
-               if (start_time.tv_sec && last_time.tv_sec) {
-                       timespecsub(&start_time, &last_time, &diff_time);
-                       condlog(4, "tick (%ld.%06lu secs)",
-                               (long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
-                       last_time = start_time;
-                       ticks = diff_time.tv_sec;
-               } else {
-                       ticks = 1;
-                       condlog(4, "tick (%d ticks)", ticks);
-               }
+               timespecsub(&start_time, &last_time, &diff_time);
+               condlog(4, "tick (%ld.%06lu secs)",
+                       (long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
+               last_time = start_time;
+               ticks = diff_time.tv_sec;
 #ifdef USE_SYSTEMD
                if (use_watchdog)
                        sd_notify(0, "WATCHDOG=1");
 #endif
+               while (checker_state != CHECKER_FINISHED) {
+                       unsigned int paths_checked = 0;
+                       struct timespec chk_start_time;
 
-               pthread_cleanup_push(cleanup_lock, &vecs->lock);
-               lock(&vecs->lock);
-               pthread_testcancel();
-               vector_foreach_slot (vecs->pathvec, pp, i) {
-                       rc = check_path(vecs, pp, ticks);
-                       if (rc < 0) {
-                               condlog(1, "%s: check_path() failed, removing",
-                                       pp->dev);
-                               vector_del_slot(vecs->pathvec, i);
-                               free_path(pp);
-                               i--;
-                       } else
-                               num_paths += rc;
+                       pthread_cleanup_push(cleanup_lock, &vecs->lock);
+                       lock(&vecs->lock);
+                       pthread_testcancel();
+                       get_monotonic_time(&chk_start_time);
+                       if (checker_state == CHECKER_STARTING) {
+                               vector_foreach_slot(vecs->pathvec, pp, i)
+                                       pp->is_checked = false;
+                               i = 0;
+                               checker_state = CHECKER_RUNNING;
+                       } else {
+                               /*
+                                * Paths could have been removed since we
+                                * dropped the lock. Find the path to continue
+                                * checking at. Since paths can be removed from
+                                * anywhere in the vector, but can only be added
+                                * at the end, the last checked path must be
+                                * between its old location, and the start or
+                                * the vector.
+                                */
+                               if (i >= VECTOR_SIZE(vecs->pathvec))
+                                       i = VECTOR_SIZE(vecs->pathvec) - 1;
+                               while ((pp = VECTOR_SLOT(vecs->pathvec, i))) {
+                                       if (pp->is_checked == true)
+                                               break;
+                                       i--;
+                               }
+                               i++;
+                       }
+                       vector_foreach_slot_after (vecs->pathvec, pp, i) {
+                               pp->is_checked = true;
+                               rc = check_path(vecs, pp, ticks);
+                               if (rc < 0) {
+                                       condlog(1, "%s: check_path() failed, removing",
+                                               pp->dev);
+                                       vector_del_slot(vecs->pathvec, i);
+                                       free_path(pp);
+                                       i--;
+                               } else
+                                       num_paths += rc;
+                               if (++paths_checked % 128 == 0 &&
+                                   (lock_has_waiters(&vecs->lock) ||
+                                    waiting_clients())) {
+                                       get_monotonic_time(&end_time);
+                                       timespecsub(&end_time, &chk_start_time,
+                                                   &diff_time);
+                                       if (diff_time.tv_sec > 0)
+                                               goto unlock;
+                               }
+                       }
+                       checker_state = CHECKER_FINISHED;
+unlock:
+                       lock_cleanup_pop(vecs->lock);
+                       if (checker_state != CHECKER_FINISHED) {
+                               /* Yield to waiters */
+                               struct timespec wait = { .tv_nsec = 10000, };
+                               nanosleep(&wait, NULL);
+                       }
                }
-               lock_cleanup_pop(vecs->lock);
 
                pthread_cleanup_push(cleanup_lock, &vecs->lock);
                lock(&vecs->lock);
                pthread_testcancel();
-               defered_failback_tick(vecs->mpvec);
+               deferred_failback_tick(vecs->mpvec);
                retry_count_tick(vecs->mpvec);
                missing_uev_wait_tick(vecs);
                ghost_delay_tick(vecs);
@@ -2642,26 +2714,23 @@ checkerloop (void *ap)
                        lock_cleanup_pop(vecs->lock);
                }
 
-               diff_time.tv_nsec = 0;
-               if (start_time.tv_sec) {
-                       get_monotonic_time(&end_time);
-                       timespecsub(&end_time, &start_time, &diff_time);
-                       if (num_paths) {
-                               unsigned int max_checkint;
-
-                               condlog(4, "checked %d path%s in %ld.%06lu secs",
-                                       num_paths, num_paths > 1 ? "s" : "",
-                                       (long)diff_time.tv_sec,
-                                       diff_time.tv_nsec / 1000);
-                               conf = get_multipath_config();
-                               max_checkint = conf->max_checkint;
-                               put_multipath_config(conf);
-                               if (diff_time.tv_sec > (time_t)max_checkint)
-                                       condlog(1, "path checkers took longer "
-                                               "than %ld seconds, consider "
-                                               "increasing max_polling_interval",
-                                               (long)diff_time.tv_sec);
-                       }
+               get_monotonic_time(&end_time);
+               timespecsub(&end_time, &start_time, &diff_time);
+               if (num_paths) {
+                       unsigned int max_checkint;
+
+                       condlog(4, "checked %d path%s in %ld.%06lu secs",
+                               num_paths, num_paths > 1 ? "s" : "",
+                               (long)diff_time.tv_sec,
+                               diff_time.tv_nsec / 1000);
+                       conf = get_multipath_config();
+                       max_checkint = conf->max_checkint;
+                       put_multipath_config(conf);
+                       if (diff_time.tv_sec > (time_t)max_checkint)
+                               condlog(1, "path checkers took longer "
+                                       "than %ld seconds, consider "
+                                       "increasing max_polling_interval",
+                                       (long)diff_time.tv_sec);
                }
 
                if (foreign_tick == 0) {
@@ -2679,12 +2748,10 @@ checkerloop (void *ap)
                if (!strict_timing)
                        sleep(1);
                else {
-                       if (diff_time.tv_nsec) {
-                               diff_time.tv_sec = 0;
-                               diff_time.tv_nsec =
-                                    1000UL * 1000 * 1000 - diff_time.tv_nsec;
-                       } else
-                               diff_time.tv_sec = 1;
+                       diff_time.tv_sec = 0;
+                       diff_time.tv_nsec =
+                            1000UL * 1000 * 1000 - diff_time.tv_nsec;
+                       normalize_timespec(&diff_time);
 
                        condlog(3, "waiting for %ld.%06lu secs",
                                (long)diff_time.tv_sec,
@@ -2940,7 +3007,7 @@ init_vecs (void)
        if (!vecs)
                return NULL;
 
-       pthread_mutex_init(&vecs->lock.mutex, NULL);
+       init_lock(&vecs->lock);
 
        return vecs;
 }
@@ -3549,7 +3616,7 @@ main (int argc, char *argv[])
        extern char *optarg;
        extern int optind;
        int arg;
-       int err;
+       int err = 0;
        int foreground = 0;
        struct config *conf;
        char *opt_k_arg = NULL;
@@ -3640,10 +3707,25 @@ main (int argc, char *argv[])
                                        c += snprintf(c, s + CMDSIZE - c,
                                                      "%s ", argv[optind]);
                                optind++;
+                               if (c >= s + CMDSIZE) {
+                                       fprintf(stderr, "multipathd command too large\n");
+                                       exit(1);
+                               }
                        }
                        c += snprintf(c, s + CMDSIZE - c, "\n");
                }
-               err = uxclnt(s, uxsock_timeout + 100);
+               if (!s) {
+                       char tmo_buf[16];
+
+                       snprintf(tmo_buf, sizeof(tmo_buf), "%d",
+                                uxsock_timeout + 100);
+                       if (execl(BINDIR "/multipathc", "multipathc",
+                                 tmo_buf, NULL) == -1) {
+                               condlog(0, "ERROR: failed to execute multipathc: %m");
+                               err = 1;
+                       }
+               } else
+                       err = uxclnt(s, uxsock_timeout + 100);
                free_config(conf);
                return err;
        }
diff --git a/multipathd/multipathc.8 b/multipathd/multipathc.8
new file mode 100644 (file)
index 0000000..6c57c6c
--- /dev/null
@@ -0,0 +1,64 @@
+.\" ----------------------------------------------------------------------------
+.\" Update the date below if you make any significant change.
+.\" Make sure there are no errors with:
+.\" groff -z -wall -b -e -t multipathd/multipathd.8
+.\"
+.\" ----------------------------------------------------------------------------
+.
+.TH MULTIPATHC 8 2022-09-03 Linux
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------------
+.
+multipathc \- Interactive client for multipathd
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------------
+.
+.B multipathc 
+.RB [\|
+.IR timeout
+.RB \|]
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------------
+.
+The \fBmultipathc\fR tool provides an interactive shell for communicating
+with the \fBmultipathd\fR daemon.
+The command \fBmultipathd -k\fR invokes \fBmultipathc\fR.
+.P
+All commands documented in \fBmultipathd(8)\fR are supported.
+The available commands can be viewed by entering '\fIhelp\fR'.
+Use \fIquit\fR, \fIexit\fR, or \fBCTRL-D\fR to exit the shell.
+Keywords can be abbreviated with the first letters (for example,
+\fIshu\fR for \fIshutdown\fR), if the abbreviation is unique.
+Some commands support pretty-printing
+using \fBprintf\fR-style format specifiers. The supported format specifiers
+can be listed with the command \fBshow wildcards\fR.
+Depending on build options, the interactive shell
+may provide command completion and history expansion features.
+.P
+The optional parameter \fBtimeout\fR specifies the timeout to wait for
+a reply from \fBmultipathd\fR, in milliseconds. The default is 4000 ms.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SEE ALSO"
+.\" ----------------------------------------------------------------------------
+.
+.BR multipathd (8)
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH AUTHORS
+.\" ----------------------------------------------------------------------------
+.
+\fImultipath-tools\fR was developed by Christophe Varoqui
+<christophe.varoqui@opensvc.com> and others.
+.\" EOF
diff --git a/multipathd/multipathc.c b/multipathd/multipathc.c
new file mode 100644 (file)
index 0000000..b3f7db0
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2022 SUSE LLC
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "mpath_cmd.h"
+#include "uxclnt.h"
+#include "vector.h"
+#include "uxsock.h"
+#include "util.h"
+#include "cli.h"
+
+#ifdef USE_LIBEDIT
+#include <editline/readline.h>
+#endif
+#ifdef USE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+/*
+ * Versions of libedit prior to 2016 were using a wrong
+ * prototype for rl_completion_entry_function in readline.h.
+ * Internally, libedit casts this to the correct type
+ * (char *)(*)(const char *, int).
+ * So we simply cast to the wrong prototype here.
+ * See http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libedit/readline/readline.h.diff?r1=1.34&r2=1.35
+ * Unfortunately, this change isn't reflected in the libedit version.
+ */
+#ifdef BROKEN_RL_COMPLETION_FUNC
+#define RL_COMP_ENTRY_CAST(x) ((int (*)(const char *, int)) (x))
+#else
+#define RL_COMP_ENTRY_CAST(x) (x)
+#endif
+
+#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT)
+static int
+key_match_fingerprint (struct key * kw, uint64_t fp)
+{
+       if (!fp)
+               return 0;
+
+       return ((fp & kw->code) == kw->code);
+}
+
+/*
+ * This is the readline completion handler
+ */
+char *
+key_generator (const char * str, int state)
+{
+       static int index, len, has_param;
+       static uint64_t rlfp;
+       struct key * kw;
+       int i;
+       struct handler *h;
+       vector v = NULL;
+       const vector keys = get_keys();
+       const vector handlers = get_handlers();
+
+       if (!state) {
+               index = 0;
+               has_param = 0;
+               rlfp = 0;
+               len = strlen(str);
+               int r = get_cmdvec(rl_line_buffer, &v);
+               /*
+                * If a word completion is in progress, we don't want
+                * to take an exact keyword match in the fingerprint.
+                * For ex "show map[tab]" would validate "map" and discard
+                * "maps" as a valid candidate.
+                */
+               if (v && len)
+                       vector_del_slot(v, VECTOR_SIZE(v) - 1);
+               /*
+                * Clean up the mess if we dropped the last slot of a 1-slot
+                * vector
+                */
+               if (v && !VECTOR_SIZE(v)) {
+                       vector_free(v);
+                       v = NULL;
+               }
+               /*
+                * If last keyword takes a param, don't even try to guess
+                */
+               if (r == EINVAL) {
+                       has_param = 1;
+                       return (strdup("(value)"));
+               }
+               /*
+                * Compute a command fingerprint to find out possible completions.
+                * Once done, the vector is useless. Free it.
+                */
+               if (v) {
+                       rlfp = fingerprint(v);
+                       free_keys(v);
+               }
+       }
+       /*
+        * No more completions for parameter placeholder.
+        * Brave souls might try to add parameter completion by walking paths and
+        * multipaths vectors.
+        */
+       if (has_param)
+               return ((char *)NULL);
+       /*
+        * Loop through keywords for completion candidates
+        */
+       vector_foreach_slot_after (keys, kw, index) {
+               if (!strncmp(kw->str, str, len)) {
+                       /*
+                        * Discard keywords already in the command line
+                        */
+                       if (key_match_fingerprint(kw, rlfp)) {
+                               struct key * curkw = find_key(str);
+                               if (!curkw || (curkw != kw))
+                                       continue;
+                       }
+                       /*
+                        * Discard keywords making syntax errors.
+                        *
+                        * nfp is the candidate fingerprint we try to
+                        * validate against all known command fingerprints.
+                        */
+                       uint64_t nfp = rlfp | kw->code;
+                       vector_foreach_slot(handlers, h, i) {
+                               if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
+                                       /*
+                                        * At least one full command is
+                                        * possible with this keyword :
+                                        * Consider it validated
+                                        */
+                                       index++;
+                                       return (strdup(kw->str));
+                               }
+                       }
+               }
+       }
+       /*
+        * No more candidates
+        */
+       return ((char *)NULL);
+}
+#endif
+
+static void print_reply(char *s)
+{
+       if (!s)
+               return;
+
+       if (isatty(1)) {
+               printf("%s", s);
+               return;
+       }
+       /* strip ANSI color markers */
+       while (*s != '\0') {
+               if ((*s == 0x1b) && (*(s+1) == '['))
+                       while ((*s++ != 'm') && (*s != '\0')) {};
+               putchar(*s++);
+       }
+}
+
+static int need_quit(char *str, size_t len)
+{
+       char *ptr, *start;
+       size_t trimed_len = len;
+
+       for (ptr = str; trimed_len && isspace(*ptr);
+            trimed_len--, ptr++)
+               ;
+
+       start = ptr;
+
+       for (ptr = str + len - 1; trimed_len && isspace(*ptr);
+            trimed_len--, ptr--)
+               ;
+
+       if ((trimed_len == 4 && !strncmp(start, "exit", 4)) ||
+           (trimed_len == 4 && !strncmp(start, "quit", 4)))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * process the client
+ */
+static void process(int fd, unsigned int timeout)
+{
+
+#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT)
+       rl_readline_name = "multipathd";
+       rl_completion_entry_function = RL_COMP_ENTRY_CAST(key_generator);
+#endif
+
+       cli_init();
+       for(;;)
+       {
+               char *line __attribute__((cleanup(cleanup_charp))) = NULL;
+               char *reply __attribute__((cleanup(cleanup_charp))) = NULL;
+               ssize_t llen;
+               int ret;
+
+#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT)
+               line = readline("multipathd> ");
+               if (!line)
+                       break;
+               llen = strlen(line);
+               if (!llen)
+                       continue;
+#else
+               size_t lsize = 0;
+
+               fputs("multipathd> ", stdout);
+               errno = 0;
+               llen = getline(&line, &lsize, stdin);
+               if (llen == -1) {
+                       if (errno != 0)
+                               fprintf(stderr, "Error in getline: %m");
+                       break;
+               }
+               if (!llen || !strcmp(line, "\n"))
+                       continue;
+#endif
+
+               if (need_quit(line, llen))
+                       break;
+
+               if (send_packet(fd, line) != 0)
+                       break;
+               ret = recv_packet(fd, &reply, timeout);
+               if (ret != 0)
+                       break;
+
+               print_reply(reply);
+
+#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT)
+               if (line && *line)
+                       add_history(line);
+#endif
+       }
+}
+
+int main (int argc, const char * const argv[])
+{
+       int fd;
+       int tmo = DEFAULT_REPLY_TIMEOUT + 100;
+       char *ep;
+
+       if (argc > 2) {
+               fprintf(stderr, "Usage: %s [timeout]\n", argv[0]);
+               return 1;
+       }
+       if (argc == 2) {
+               tmo = strtol(argv[1], &ep, 10);
+               if (*argv[1] == '\0' || *ep != '\0' || tmo < 0) {
+                       fprintf(stderr, "ERROR: invalid timeout value\n");
+                       return 1;
+               }
+       }
+
+       fd = mpath_connect();
+       if (fd == -1) {
+               fprintf(stderr, "ERROR: failed to connect to multipathd\n");
+               return 1;
+       }
+
+       process(fd, tmo);
+       mpath_disconnect(fd);
+       return 0;
+}
+
+#define HANDLER(x) NULL
+#include "callbacks.c"
index 1e318bdc79da9b9eca1ea5af7e6ddbd055fd9447..bdf102eb3e1de6f664e09534f3e5046ec1ad68b4 100644 (file)
@@ -5,7 +5,7 @@
 .\"
 .\" ----------------------------------------------------------------------------
 .
-.TH MULTIPATHD 8 2016-10-27 Linux
+.TH MULTIPATHD 8 2022-09-03 Linux
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -20,14 +20,23 @@ multipathd \- Multipath daemon.
 .\" ----------------------------------------------------------------------------
 .
 .B multipathd
-.RB [\| \-d | \-k \|]
+.RB [\| \-d \|]
 .RB [\| \-s \|]
 .RB [\| \-v\ \c
 .IR verbosity \|]
 .RB [\| \-B \|]
 .RB [\| \-w \|]
-.
-.
+.LP
+.B multipathd
+.RB [\| \-v\ \c
+.IR verbosity \|]
+.B -k\fIcommand\fR
+.LP
+.B multipathd
+.RB [\| \-v\ \c
+.IR verbosity \|]
+.B -k
+
 .\" ----------------------------------------------------------------------------
 .SH DESCRIPTION
 .\" ----------------------------------------------------------------------------
@@ -36,9 +45,9 @@ The \fBmultipathd\fR daemon is in charge of checking for failed paths. When this
 happens, it will reconfigure the multipath map the path belongs to, so that this
 map regains its maximum performance and redundancy.
 
-This daemon executes the external \fBmultipath\fR tool when events occur.
-In turn, the multipath tool signals the multipathd daemon when it is done with
-devmap reconfiguration, so that it can refresh its failed path list.
+With the \fB-k\fR option, \fBmultipathd\fR acts as a client utility that
+sends commands to a running instance of the multipathd daemon (see
+\fBCOMMANDS\fR below).
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -66,10 +75,16 @@ bindings file. If a \fIuser_friendly_name\fR doesn't already exist for a device,
 will use its WWID as its alias.
 .
 .TP
+.B \-k\fIcommand\fB
+multipathd executes the given command (see \fBCOMMANDS\fR below). If the
+command contains whitespace or shell special characters, it needs to be quoted
+like in \fImultipathd -k'show topology'\fR. No whitespace is allowed between
+the \fB-k\fR and the command string.
+.
+.TP
 .B \-k
-multipathd will enter interactive mode. From this mode, the available commands can
-be viewed by entering '\fIhelp\fR'. When you are finished entering commands, press
-\fBCTRL-D\fR to quit.
+multipathd executes the \fBmultipathc\fR interactive shell for entering
+commands (see \fBCOMMANDS\fR below).
 .
 .TP
 .B \-n
@@ -346,10 +361,12 @@ Overrides the \fImax_fds\fR configuration setting.
 .SH "SEE ALSO"
 .\" ----------------------------------------------------------------------------
 .
+.BR multipathc (8),
 .BR multipath (8),
-.BR kpartx (8),
+.BR kpartx (8)
+.RE
 .BR sd_notify (3),
-.BR system.service (5).
+.BR systemd.service (5).
 .
 .
 .\" ----------------------------------------------------------------------------
index b1b058bd586e95d968e6da9c2fb80defcbdfc342..c3ae5db602eee4499d03aa76e6a0cf8d9a45755b 100644 (file)
  * Copyright (c) 2005 Benjamin Marzinski, Redhat
  */
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <fcntl.h>
 #include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <poll.h>
-#include <readline/readline.h>
-#include <readline/history.h>
 
 #include "mpath_cmd.h"
 #include "uxsock.h"
-#include "defaults.h"
-
-#include "vector.h"
-#include "cli.h"
 #include "uxclnt.h"
 
-static void print_reply(char *s)
-{
-       if (!s)
-               return;
-
-       if (isatty(1)) {
-               printf("%s", s);
-               return;
-       }
-       /* strip ANSI color markers */
-       while (*s != '\0') {
-               if ((*s == 0x1b) && (*(s+1) == '['))
-                       while ((*s++ != 'm') && (*s != '\0')) {};
-               putchar(*s++);
-       }
-}
-
-static int need_quit(char *str, size_t len)
-{
-       char *ptr, *start;
-       size_t trimed_len = len;
-
-       for (ptr = str; trimed_len && isspace(*ptr);
-            trimed_len--, ptr++)
-               ;
-
-       start = ptr;
-
-       for (ptr = str + len - 1; trimed_len && isspace(*ptr);
-            trimed_len--, ptr--)
-               ;
-
-       if ((trimed_len == 4 && !strncmp(start, "exit", 4)) ||
-           (trimed_len == 4 && !strncmp(start, "quit", 4)))
-               return 1;
-
-       return 0;
-}
-
-/*
- * process the client
- */
-static void process(int fd, unsigned int timeout)
-{
-       char *line;
-       char *reply;
-       int ret;
-
-       cli_init();
-       rl_readline_name = "multipathd";
-       rl_completion_entry_function = key_generator;
-       while ((line = readline("multipathd> "))) {
-               size_t llen = strlen(line);
-
-               if (!llen) {
-                       free(line);
-                       continue;
-               }
-
-               if (need_quit(line, llen))
-                       break;
-
-               if (send_packet(fd, line) != 0) break;
-               ret = recv_packet(fd, &reply, timeout);
-               if (ret != 0) break;
-
-               print_reply(reply);
-
-               if (line && *line)
-                       add_history(line);
-
-               free(line);
-               free(reply);
-       }
-}
-
 static int process_req(int fd, char * inbuf, unsigned int timeout)
 {
        char *reply;
@@ -133,14 +44,14 @@ int uxclnt(char * inbuf, unsigned int timeout)
 {
        int fd, ret = 0;
 
+       if (!inbuf)
+               return 1;
        fd = mpath_connect();
        if (fd == -1)
-               exit(1);
+               return 1;
+
+       ret = process_req(fd, inbuf, timeout);
 
-       if (inbuf)
-               ret = process_req(fd, inbuf, timeout);
-       else
-               process(fd, timeout);
        mpath_disconnect(fd);
        return ret;
 }
index 645e356c9e7eaa08dc8ee09b4b5bd418173c1515..23cb123d3cd1be07e8f09bfbeb5784a77c7ce00d 100644 (file)
@@ -91,6 +91,7 @@ static LIST_HEAD(clients);
 static struct pollfd *polls;
 static int notify_fd = -1;
 static int idle_fd = -1;
+static bool clients_need_lock = false;
 
 static bool _socket_client_is_root(int fd)
 {
@@ -309,15 +310,22 @@ static struct timespec *get_soonest_timeout(struct timespec *ts)
        return ts;
 }
 
-static bool need_vecs_lock(void)
+bool waiting_clients(void)
+{
+       return clients_need_lock;
+}
+
+static void check_for_locked_work(struct client *skip)
 {
        struct client *c;
 
        list_for_each_entry(c, &clients, node) {
-               if (c->state == CLT_LOCKED_WORK)
-                       return true;
+               if (c != skip && c->state == CLT_LOCKED_WORK) {
+                       clients_need_lock = true;
+                       return;
+               }
        }
-       return false;
+       clients_need_lock = false;
 }
 
 static int parse_cmd(struct client *c)
@@ -397,10 +405,11 @@ static void set_client_state(struct client *c, int state)
        case CLT_RECV:
                reset_strbuf(&c->reply);
                memset(c->cmd, '\0', sizeof(c->cmd));
-               c->expires = ts_zero;
                c->error = 0;
                /* fallthrough */
        case CLT_SEND:
+               /* no timeout while waiting for the client or sending a reply */
+               c->expires = ts_zero;
                /* reuse these fields for next data transfer */
                c->len = c->cmd_len = 0;
                break;
@@ -494,6 +503,7 @@ static int client_state_machine(struct client *c, struct vectors *vecs,
                        /* don't use cleanup_lock(), lest we wakeup ourselves */
                        pthread_cleanup_push_cast(__unlock, &vecs->lock);
                        c->error = execute_handler(c, vecs);
+                       check_for_locked_work(c);
                        pthread_cleanup_pop(1);
                        condlog(4, "%s: cli[%d] grabbed lock", __func__, c->fd);
                        free_keys(c->cmdvec);
@@ -661,7 +671,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
                        polls[POLLFD_NOTIFY].events = POLLIN;
 
                polls[POLLFD_IDLE].fd = idle_fd;
-               if (need_vecs_lock())
+               check_for_locked_work(NULL);
+               if (clients_need_lock)
                        polls[POLLFD_IDLE].events = POLLIN;
                else
                        polls[POLLFD_IDLE].events = 0;
index 60c3a2c7b5490c6c541c48cafb29c9248c25e9a9..3e45930bd20829ecbeb898a5b9da8dc353004e71 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <stdbool.h>
 
+bool waiting_clients(void);
 void uxsock_cleanup(void *arg);
 void *uxsock_listen(long ux_sock,
                    void * trigger_data);
index 2a2214652617876335992f9610b0cc386a2e66e0..527936980f430acf751cee06378fea937e52fab5 100644 (file)
@@ -24,7 +24,7 @@
 #include "main.h"
 
 pthread_attr_t waiter_attr;
-struct mutex_lock waiter_lock = { .mutex = PTHREAD_MUTEX_INITIALIZER };
+static pthread_mutex_t waiter_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static struct event_thread *alloc_waiter (void)
 {
@@ -65,11 +65,11 @@ void stop_waiter_thread (struct multipath *mpp)
                (unsigned long)mpp->waiter);
        thread = mpp->waiter;
        mpp->waiter = (pthread_t)0;
-       pthread_cleanup_push(cleanup_lock, &waiter_lock);
-       lock(&waiter_lock);
+       pthread_cleanup_push(cleanup_mutex, &waiter_lock);
+       pthread_mutex_lock(&waiter_lock);
        pthread_kill(thread, SIGUSR2);
        pthread_cancel(thread);
-       lock_cleanup_pop(&waiter_lock);
+       pthread_cleanup_pop(1);
 }
 
 /*
@@ -126,10 +126,10 @@ static int waiteventloop (struct event_thread *waiter)
        waiter->dmt = NULL;
 
        if (!r) { /* wait interrupted by signal. check for cancellation */
-               pthread_cleanup_push(cleanup_lock, &waiter_lock);
-               lock(&waiter_lock);
+               pthread_cleanup_push(cleanup_mutex, &waiter_lock);
+               pthread_mutex_lock(&waiter_lock);
                pthread_testcancel();
-               lock_cleanup_pop(&waiter_lock);
+               pthread_cleanup_pop(1);
                return 1; /* If we weren't cancelled, just reschedule */
        }
 
index d20ef2362f6ad36baf062cba6d7ad41b0962a183..109ea75bb3212566cb7ba981bc06a066485d61ee 100644 (file)
@@ -11,12 +11,12 @@ TEST_MISSING_INITIALIZERS = $(shell \
        || echo -Wno-missing-field-initializers)
 W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
 
-CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir) -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
 CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
-LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
+LIBDEPS += -L. -L $(mpathutildir) -L$(mpathcmddir) -lmultipath -lmpathutil -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
-        alias directio valid devt mpathvalid strbuf
+        alias directio valid devt mpathvalid strbuf sysfs
 HELPERS := test-lib.o test-log.o
 
 .SILENT: $(TESTS:%=%.o)
@@ -48,43 +48,46 @@ mpathvalid-test_FLAGS := -I$(mpathvaliddir)
 #    unit test file, e.g. "config-test.o", in XYZ-test_OBJDEPS
 # XYZ-test_LIBDEPS: Additional libs to link for this test
 
-dmevents-test_OBJDEPS = ../libmultipath/devmapper.o
+dmevents-test_OBJDEPS = $(multipathdir)/devmapper.o
 dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu
 hwtable-test_TESTDEPS := test-lib.o
-hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \
-       ../libmultipath/structs.o ../libmultipath/propsel.o
+hwtable-test_OBJDEPS := $(multipathdir)/discovery.o $(multipathdir)/blacklist.o \
+       $(multipathdir)/structs.o $(multipathdir)/propsel.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_TESTDEPS := test-log.o
-blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
+blacklist-test_OBJDEPS := $(multipathdir)/blacklist.o
 blacklist-test_LIBDEPS := -ludev
-vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
+vpd-test_OBJDEPS :=  $(multipathdir)/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
 alias-test_LIBDEPS := -lpthread -ldl
-valid-test_OBJDEPS := ../libmultipath/valid.o ../libmultipath/discovery.o
+valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o
 valid-test_LIBDEPS := -ludev -lpthread -ldl
 devt-test_LIBDEPS := -ludev
 mpathvalid-test_LIBDEPS := -ludev -lpthread -ldl
-mpathvalid-test_OBJDEPS := ../libmpathvalid/mpath_valid.o
+mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o
 ifneq ($(DIO_TEST_DEV),)
 directio-test_LIBDEPS := -laio
 endif
-strbuf-test_OBJDEPS := ../libmultipath/strbuf.o
+strbuf-test_OBJDEPS := $(mpathutildir)/strbuf.o
+sysfs-test_TESTDEPS := test-log.o
+sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o
+sysfs-test_LIBDEPS := -ludev -lpthread -ldl
 
 %.o: %.c
        $(CC) $(CPPFLAGS) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
        mkdir -p lib
-       ln ../libmultipath/*/*.so lib
+       cd lib && ln -s ../$(multipathdir)/*/*.so .
 
 %.out: %-test lib/libchecktur.so
        @echo == running $< ==
-       @LD_LIBRARY_PATH=.:$(mpathcmddir) ./$< >$@
+       @LD_LIBRARY_PATH=.:$(mpathutildir):$(mpathcmddir) ./$< >$@ 2>&1
 
 %.vgr:  %-test lib/libchecktur.so
        @echo == running valgrind for $< ==
-       @LD_LIBRARY_PATH=.:$(mpathcmddir) \
+       @LD_LIBRARY_PATH=.:$(mpathutildir):$(mpathcmddir) \
                valgrind --leak-check=full --error-exitcode=128 ./$< >$@ 2>&1
 
 OBJS = $(TESTS:%=%.o) $(HELPERS)
@@ -110,14 +113,14 @@ dep_clean:
        @sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' $< | \
                sort -u | tr '\n' ' ' >$@
 
-libmultipath.so.0:
+libmultipath.so.0: $(multipathdir)/libmultipath.so.0
        make -C $(multipathdir) configdir=$(TESTDIR)/conf.d plugindir=$(TESTDIR)/lib test-lib
 
 # COLON will get expanded during second expansion below
 COLON:=:
 .SECONDEXPANSION:
 %-test:        %.o %.o.wrap $$($$@_OBJDEPS) $$($$@_TESTDEPS) $$($$@_TESTDEPS$$(COLON).o=.o.wrap) \
-               libmultipath.so.0 Makefile
+               libmultipath.so.0 $(mpathutildir)/libmpathutil.so.0 $(mpathcmddir)/libmpathcmd.so.0 Makefile
        $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $< $($@_TESTDEPS) $($@_OBJDEPS) \
                $(LIBDEPS) $($@_LIBDEPS) \
                $(shell cat $<.wrap) $(foreach dep,$($@_TESTDEPS),$(shell cat $(dep).wrap))
index 20ccc47aa7b9143c906ceaad503442c278cee024..01fdef28a1cb31b3d99ae9a97a2cc0963056174a 100644 (file)
@@ -497,7 +497,7 @@ static void test_free_with_pending(void **state)
         do_libcheck_reset(1);
 }
 
-/* test removing orpahed aio_group on free */
+/* test removing orphaned aio_group on free */
 static void test_orphaned_aio_group(void **state)
 {
        struct checker c[AIO_GROUP_SIZE] = {{.cls = NULL}};
index bfaf613f80e801fc5963958b1d35eabf7fbbb61d..334b75e8ca7f956d9d438c595af5107cb62550eb 100644 (file)
@@ -29,7 +29,7 @@
 
 #define N_CONF_FILES 2
 
-static const char tmplate[] = "/tmp/hwtable-XXXXXX";
+static const char template[] = "/tmp/hwtable-XXXXXX";
 
 struct key_value {
        const char *key;
@@ -136,7 +136,7 @@ static int setup(void **state)
        if (hwt == NULL)
                return -1;
 
-       snprintf(buf, sizeof(buf), "%s", tmplate);
+       snprintf(buf, sizeof(buf), "%s", template);
        if (mkdtemp(buf) == NULL) {
                condlog(0, "mkdtemp: %s", strerror(errno));
                goto err;
@@ -255,7 +255,7 @@ static void write_defaults(const struct hwt_state *hwt)
                { "detect_prio", "no" },
                { "detect_checker", "no" },
        };
-       char buf[sizeof(tmplate) + sizeof(bindings_name)];
+       char buf[sizeof(template) + sizeof(bindings_name)];
        char dirbuf[PATH_MAX];
 
        snprintf(buf, sizeof(buf), "%s/%s", hwt->tmpname, bindings_name);
@@ -308,7 +308,7 @@ static void write_device(FILE *ff, int nkv, const struct key_value *kv)
 }
 
 /*
- * Some macros to avoid boilerplace code
+ * Some macros to avoid boilerplate code
  */
 
 #define CHECK_STATE(state) ({ \
@@ -448,7 +448,7 @@ static const struct key_value npr_queue = { _no_path_retry, "queue" };
 /***** BEGIN TESTS SECTION *****/
 
 /*
- * Dump the configuration, subistitute the dumped configuration
+ * Dump the configuration, substitute the dumped configuration
  * for the current one, and verify that the result is identical.
  */
 static void replicate_config(const struct hwt_state *hwt, bool local)
index 0230a88f7abbfaf0c2d00e50608000512366b04b..df66ed6ae14bcd121f4785aadd261540e2af6adb 100644 (file)
@@ -399,7 +399,7 @@ static void test_mpathvalid_is_path_good3(void **state)
        free(wwid);
 }
 
-/* mabybe valid with no matching paths */
+/* maybe valid with no matching paths */
 static void test_mpathvalid_is_path_good4(void **state)
 {
        const char *wwids[] = { "WWID_A", "WWID_B", "WWID_C", "WWID_D" };
index f116d12c918ae8b48c54241f83add70c497c9a3b..43be831fb23aca447f6b27bcd2c1b8a91cf347e9 100644 (file)
@@ -191,7 +191,7 @@ verify_pathgroups(struct multipath *mp, struct path *pp, int **groups,
                        /* Test names instead of pointers to get a more
                         * useful error message */
                        assert_string_equal(pgp_path->dev, pp_path->dev);
-                       /* This test is just a backkup in case the
+                       /* This test is just a backup in case the
                         * something wenth wrong naming the paths */
                        assert_ptr_equal(pgp_path, pp_path);
                }
diff --git a/tests/sysfs.c b/tests/sysfs.c
new file mode 100644 (file)
index 0000000..0ec135b
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#include <libudev.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "debug.h"
+#include "globals.c"
+#include "test-log.h"
+#include "sysfs.h"
+#include "util.h"
+
+#define TEST_FD 123
+
+char *__wrap_udev_device_get_syspath(struct udev_device *ud)
+{
+       char *val  = mock_ptr_type(char *);
+
+       return val;
+}
+
+int __wrap_open(const char *pathname, int flags)
+{
+       int ret;
+
+       check_expected(pathname);
+       check_expected(flags);
+       ret = mock_type(int);
+       return ret;
+}
+
+ssize_t __wrap_read(int fd, void *buf, size_t count)
+{
+       ssize_t ret;
+       char *val;
+
+       check_expected(fd);
+       check_expected(count);
+       ret = mock_type(int);
+       val = mock_ptr_type(char *);
+       if (ret >= (ssize_t)count)
+               ret = count;
+       if (ret >= 0 && val) {
+               fprintf(stderr, "%s: '%s' -> %zd\n", __func__, val, ret);
+               memcpy(buf, val, ret);
+       }
+       return ret;
+}
+
+ssize_t __wrap_write(int fd, void *buf, size_t count)
+{
+       ssize_t ret;
+
+       check_expected(fd);
+       check_expected(count);
+       ret = mock_type(int);
+       if (ret >= (ssize_t)count)
+               ret = count;
+       return ret;
+}
+
+int __real_close(int fd);
+int __wrap_close(int fd) {
+       if (fd != TEST_FD)
+               return __real_close(fd);
+       return mock_type(int);
+}
+
+static int setup(void **state)
+{
+       udev = udev_new();
+       return 0;
+}
+
+static int teardown(void **state)
+{
+       udev_unref(udev);
+       return 0;
+}
+
+static void expect_sagv_invalid(void)
+{
+       expect_condlog(1, "__sysfs_attr_get_value: invalid parameters");
+}
+
+static void test_sagv_invalid(void **state)
+{
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_attr_get_value(NULL, NULL, NULL, 0), -EINVAL);
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_bin_attr_get_value(NULL, NULL, NULL, 0), -EINVAL);
+
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_attr_get_value(NULL, (void *)state, (void *)state, 1),
+                        -EINVAL);
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_bin_attr_get_value(NULL, (void *)state, (void *)state, 1),
+                        -EINVAL);
+
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_attr_get_value((void *)state, NULL, (void *)state, 1),
+                        -EINVAL);
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, NULL, (void *)state, 1),
+                        -EINVAL);
+
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state, NULL, 1),
+                        -EINVAL);
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state, NULL, 1),
+                        -EINVAL);
+
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state,
+                                             (void *)state, 0), -EINVAL);
+       expect_sagv_invalid();
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state,
+                                                 (void *)state, 0), -EINVAL);
+}
+
+static void test_sagv_bad_udev(void **state)
+{
+       will_return(__wrap_udev_device_get_syspath, NULL);
+       expect_condlog(3, "__sysfs_attr_get_value: invalid udevice");
+       assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state,
+                                             (void *)state, 1), -EINVAL);
+
+       will_return(__wrap_udev_device_get_syspath, NULL);
+       expect_condlog(3, "__sysfs_attr_get_value: invalid udevice");
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state,
+                                                 (void *)state, 1), -EINVAL);
+}
+
+static void test_sagv_bad_snprintf(void **state)
+{
+       char longstr[PATH_MAX + 1];
+       char buf[1];
+
+       memset(longstr, 'a', sizeof(longstr) - 1);
+       longstr[sizeof(longstr) - 1] = '\0';
+
+       will_return(__wrap_udev_device_get_syspath, "/foo");
+       expect_condlog(3, "__sysfs_attr_get_value: devpath overflow");
+       assert_int_equal(sysfs_attr_get_value((void *)state, longstr,
+                                             buf, sizeof(buf)), -EOVERFLOW);
+       will_return(__wrap_udev_device_get_syspath, "/foo");
+       expect_condlog(3, "__sysfs_attr_get_value: devpath overflow");
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, longstr,
+                                                 (unsigned char *)buf, sizeof(buf)),
+                        -EOVERFLOW);
+}
+
+static void test_sagv_open_fail(void **state)
+{
+       char buf[1];
+
+       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);
+       errno = ENOENT;
+       will_return(__wrap_open, -1);
+       expect_condlog(3, "__sysfs_attr_get_value: attribute '/foo/bar' can not be opened");
+       assert_int_equal(sysfs_attr_get_value((void *)state, "bar",
+                                             buf, sizeof(buf)), -ENOENT);
+}
+
+static void test_sagv_read_fail(void **state)
+{
+       char buf[1];
+
+       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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_read, fd, TEST_FD);
+       expect_value(__wrap_read, count, sizeof(buf));
+       errno = EISDIR;
+       will_return(__wrap_read, -1);
+       will_return(__wrap_read, NULL);
+       expect_condlog(3, "__sysfs_attr_get_value: read from /foo/bar failed:");
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_attr_get_value((void *)state, "bar",
+                                             buf, sizeof(buf)), -EISDIR);
+
+       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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_read, fd, TEST_FD);
+       expect_value(__wrap_read, count, sizeof(buf));
+       errno = EPERM;
+       will_return(__wrap_read, -1);
+       will_return(__wrap_read, NULL);
+       expect_condlog(3, "__sysfs_attr_get_value: read from /foo/baz failed:");
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, "baz",
+                                                 (unsigned char *)buf, sizeof(buf)),
+                        -EPERM);
+
+}
+
+static void _test_sagv_read(void **state, unsigned int bufsz)
+{
+       char buf[16];
+       char input[] = "01234567";
+       unsigned int n, trunc;
+
+       assert_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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_read, fd, TEST_FD);
+       expect_value(__wrap_read, count, bufsz);
+       will_return(__wrap_read, sizeof(input) - 1);
+       will_return(__wrap_read, input);
+
+       /* If the buffer is too small, input will be truncated by a 0 byte */
+       if (bufsz <= sizeof(input) - 1) {
+               n = bufsz;
+               trunc = 1;
+               expect_condlog(3, "__sysfs_attr_get_value: overflow reading from /foo/bar");
+       } else {
+               n = sizeof(input) - 1;
+               trunc = 0;
+       }
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_attr_get_value((void *)state, "bar",
+                                             buf, bufsz), n);
+       assert_memory_equal(buf, input, n - trunc);
+       assert_int_equal(buf[n - trunc], '\0');
+
+       /* Binary input is not truncated */
+       memset(buf, '.', sizeof(buf));
+       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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_read, fd, TEST_FD);
+       expect_value(__wrap_read, count, bufsz);
+       will_return(__wrap_read, sizeof(input) - 1);
+       will_return(__wrap_read, input);
+       will_return(__wrap_close, 0);
+       n = bufsz < sizeof(input) - 1 ? bufsz : sizeof(input) - 1;
+       assert_int_equal(sysfs_bin_attr_get_value((void *)state, "baz",
+                                                 (unsigned char *)buf,
+                                                 bufsz),
+                        n);
+       assert_memory_equal(buf, input, n);
+}
+
+static void test_sagv_read_overflow_8(void **state)
+{
+       _test_sagv_read(state, 8);
+}
+
+static void test_sagv_read_overflow_4(void **state)
+{
+       _test_sagv_read(state, 4);
+}
+
+static void test_sagv_read_overflow_1(void **state)
+{
+       _test_sagv_read(state, 1);
+}
+
+static void test_sagv_read_good_9(void **state)
+{
+       _test_sagv_read(state, 9);
+}
+
+static void test_sagv_read_good_15(void **state)
+{
+       _test_sagv_read(state, 15);
+}
+
+static void _test_sagv_read_zeroes(void **state, unsigned int bufsz)
+{
+       char buf[16];
+       char input[] = { '\0','\0','\0','\0','\0','\0','\0','\0' };
+       unsigned int n;
+
+       assert_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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_read, fd, TEST_FD);
+       expect_value(__wrap_read, count, bufsz);
+       will_return(__wrap_read, sizeof(input) - 1);
+       will_return(__wrap_read, input);
+
+       if (bufsz <= sizeof(input) - 1) {
+               n = bufsz;
+               expect_condlog(3, "__sysfs_attr_get_value: overflow reading from /foo/bar");
+       } else
+               n = 0;
+
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_attr_get_value((void *)state, "bar",
+                                             buf, bufsz), n);
+
+       /*
+        * The return value of sysfs_attr_get_value ignores zero bytes,
+        * but the read data should have been copied to the buffer
+        */
+       assert_memory_equal(buf, input, n == 0 ? bufsz : n);
+}
+
+static void test_sagv_read_zeroes_4(void **state)
+{
+       _test_sagv_read_zeroes(state, 4);
+}
+
+static void expect_sasv_invalid(void)
+{
+       expect_condlog(1, "sysfs_attr_set_value: invalid parameters");
+}
+
+static void test_sasv_invalid(void **state)
+{
+       expect_sasv_invalid();
+       assert_int_equal(sysfs_attr_set_value(NULL, NULL, NULL, 0), -EINVAL);
+
+       expect_sasv_invalid();
+       assert_int_equal(sysfs_attr_set_value(NULL, (void *)state, (void *)state, 1),
+                        -EINVAL);
+
+       expect_sasv_invalid();
+       assert_int_equal(sysfs_attr_set_value((void *)state, NULL, (void *)state, 1),
+                        -EINVAL);
+
+       expect_sasv_invalid();
+       assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state, NULL, 1),
+                        -EINVAL);
+
+       expect_sasv_invalid();
+       assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state,
+                                             (void *)state, 0), -EINVAL);
+}
+
+static void test_sasv_bad_udev(void **state)
+{
+       will_return(__wrap_udev_device_get_syspath, NULL);
+       expect_condlog(3, "sysfs_attr_set_value: invalid udevice");
+       assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state,
+                                             (void *)state, 1), -EINVAL);
+}
+
+static void test_sasv_bad_snprintf(void **state)
+{
+       char longstr[PATH_MAX + 1];
+       char buf[1];
+
+       memset(longstr, 'a', sizeof(longstr) - 1);
+       longstr[sizeof(longstr) - 1] = '\0';
+
+       will_return(__wrap_udev_device_get_syspath, "/foo");
+       expect_condlog(3, "sysfs_attr_set_value: devpath overflow");
+       assert_int_equal(sysfs_attr_set_value((void *)state, longstr,
+                                             buf, sizeof(buf)), -EOVERFLOW);
+}
+
+static void test_sasv_open_fail(void **state)
+{
+       char buf[1];
+
+       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);
+       errno = EPERM;
+       will_return(__wrap_open, -1);
+       expect_condlog(3, "sysfs_attr_set_value: attribute '/foo/bar' can not be opened");
+       assert_int_equal(sysfs_attr_set_value((void *)state, "bar",
+                                             buf, sizeof(buf)), -EPERM);
+}
+
+static void test_sasv_write_fail(void **state)
+{
+       char buf[1];
+
+       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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_write, fd, TEST_FD);
+       expect_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:");
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_attr_set_value((void *)state, "bar",
+                                             buf, sizeof(buf)), -EISDIR);
+
+}
+
+static void _test_sasv_write(void **state, unsigned int n_written)
+{
+       char buf[8];
+
+       assert_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);
+       will_return(__wrap_open, TEST_FD);
+       expect_value(__wrap_write, fd, TEST_FD);
+       expect_value(__wrap_write, count, sizeof(buf));
+       will_return(__wrap_write, n_written);
+
+       if (n_written < sizeof(buf))
+               expect_condlog(3, "sysfs_attr_set_value: underflow writing");
+       will_return(__wrap_close, 0);
+       assert_int_equal(sysfs_attr_set_value((void *)state, "bar",
+                                             buf, sizeof(buf)),
+                        n_written);
+}
+
+static void test_sasv_write_0(void **state)
+{
+       _test_sasv_write(state, 0);
+}
+
+static void test_sasv_write_4(void **state)
+{
+       _test_sasv_write(state, 4);
+}
+
+static void test_sasv_write_7(void **state)
+{
+       _test_sasv_write(state, 7);
+}
+
+static void test_sasv_write_8(void **state)
+{
+       _test_sasv_write(state, 8);
+}
+
+static int test_sysfs(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_sagv_invalid),
+               cmocka_unit_test(test_sagv_bad_udev),
+               cmocka_unit_test(test_sagv_bad_snprintf),
+               cmocka_unit_test(test_sagv_open_fail),
+               cmocka_unit_test(test_sagv_read_fail),
+               cmocka_unit_test(test_sagv_read_overflow_1),
+               cmocka_unit_test(test_sagv_read_overflow_4),
+               cmocka_unit_test(test_sagv_read_overflow_8),
+               cmocka_unit_test(test_sagv_read_good_9),
+               cmocka_unit_test(test_sagv_read_good_15),
+               cmocka_unit_test(test_sagv_read_zeroes_4),
+               cmocka_unit_test(test_sasv_invalid),
+               cmocka_unit_test(test_sasv_bad_udev),
+               cmocka_unit_test(test_sasv_bad_snprintf),
+               cmocka_unit_test(test_sasv_open_fail),
+               cmocka_unit_test(test_sasv_write_fail),
+               cmocka_unit_test(test_sasv_write_0),
+               cmocka_unit_test(test_sasv_write_4),
+               cmocka_unit_test(test_sasv_write_7),
+               cmocka_unit_test(test_sasv_write_8),
+       };
+
+       return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       init_test_verbosity(4);
+       ret += test_sysfs();
+       return ret;
+}
index 6dd3ee8893a2f70be8caed71dc7c396f62786486..0bc49d53a67af8dc4f0587f17910b8b6360ede41 100644 (file)
@@ -334,7 +334,6 @@ void mock_pathinfo(int mask, const struct mocked_path *mp)
        if (mask & DI_SERIAL) {
                will_return(__wrap_udev_device_get_subsystem, "scsi");
                will_return(__wrap_udev_device_get_sysname, hbtl);
-               will_return(__wrap_udev_device_get_sysname, hbtl);
        }
 
        if (mask & DI_WWID) {
index 14f25b9bc4901a4915fe485649b3281006c94c96..c17458723c4177590176ace07384b5d9af9340b9 100644 (file)
@@ -6,6 +6,8 @@
 #include <cmocka.h>
 #include "log.h"
 #include "test-log.h"
+#include "debug.h"
+
 
 __attribute__((format(printf, 2, 0)))
 void __wrap_dlog (int prio, const char * fmt, ...)
@@ -18,12 +20,15 @@ void __wrap_dlog (int prio, const char * fmt, ...)
        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 *);
        assert_memory_equal(buff, expected, strlen(expected));
 }
 
 void expect_condlog(int prio, char *string)
 {
+       if (prio > MAX_VERBOSITY || prio > libmp_verbosity)
+               return;
        expect_value(__wrap_dlog, prio, prio);
        will_return(__wrap_dlog, string);
 }
index e7393a1c4b77b4b2e08f6c5d97ad10bfa8cd4263..398b771e39160d12f911fa291f616c1af6833da2 100644 (file)
@@ -293,7 +293,7 @@ static void test_sysfs_is_multipathed(void **state)
 
        memset(&pp, 0, sizeof(pp));
        conf.find_multipaths = FIND_MULTIPATHS_STRICT;
-       /* test for already existing multiapthed device */
+       /* test for already existing multipathed device */
        will_return(__wrap_sysfs_is_multipathed, true);
        will_return(__wrap_sysfs_is_multipathed, wwid);
        assert_int_equal(is_path_valid(name, &conf, &pp, true),
@@ -452,7 +452,7 @@ static void test_greedy(void **state)
        assert_string_equal(pp.dev, name);
        assert_ptr_equal(pp.udev, &test_udev);
        assert_string_equal(pp.wwid, wwid);
-       /* test greedy success without checking multiapthd */
+       /* test greedy success without checking multipathd */
        memset(&pp, 0, sizeof(pp));
        setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED);
        assert_int_equal(is_path_valid(name, &conf, &pp, false),
index 577c8f05e5b02ea8f332d43b6d24b30c248f4eb1..1633b3181bd9a7509af419b65d533cfcb5babb5e 100644 (file)
@@ -1075,7 +1075,7 @@ typedef
 
 /* Use these to write the name of your wrapper.  NOTE: duplicates
    VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h.  NOTE also: inserts
-   the default behaviour equivalance class tag "0000" into the name.
+   the default behaviour equivalence class tag "0000" into the name.
    See pub_tool_redir.h for details -- normally you don't need to
    think about this, though. */
 
@@ -1620,11 +1620,11 @@ typedef
    and say that %r15 is trashed instead.  gcc seems happy to go with
    that.
 
-   Oh .. and this all needs to be conditionalised so that it is
+   Oh .. and this all needs to be conditionalized so that it is
    unchanged from before this commit, when compiled with older gccs
    that don't support __builtin_dwarf_cfa.  Furthermore, since
    this header file is freestanding, it has to be independent of
-   config.h, and so the following conditionalisation cannot depend on
+   config.h, and so the following conditionalization cannot depend on
    configure time checks.
 
    Although it's not clear from
@@ -1673,7 +1673,7 @@ typedef
 /* NB 9 Sept 07.  There is a nasty kludge here in all these CALL_FN_
    macros.  In order not to trash the stack redzone, we need to drop
    %rsp by 128 before the hidden call, and restore afterwards.  The
-   nastyness is that it is only by luck that the stack still appears
+   nastiness is that it is only by luck that the stack still appears
    to be unwindable during the hidden call - since then the behaviour
    of any routine using this macro does not match what the CFI data
    says.  Sigh.