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
jobs:
bionic:
runs-on: ubuntu-18.04
+ strategy:
+ matrix:
+ rl: ['', 'libreadline', 'libedit']
steps:
- uses: actions/checkout@v2
- name: mpath
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
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
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
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
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
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
runs-on: ubuntu-20.04
strategy:
matrix:
- os: [buster]
+ os: [bullseye]
arch: ['ppc64le', 'aarch64', 's390x']
container: mwilck/multipath-build-${{ matrix.os }}-${{ matrix.arch }}
steps:
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' }}
needs: build
strategy:
matrix:
- os: [buster]
+ os: [bullseye]
arch: ['ppc64le', 'aarch64', 's390x']
steps:
- name: get binaries
- 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
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
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: >
userspace-rcu-devel
json-c-devel
libcmocka-devel
+ libedit-devel
- name: checkout
uses: actions/checkout@v1
- name: build and test
kpartx/kpartx
multipath/multipath
multipathd/multipathd
+multipathd/multipathc
mpathpersist/mpathpersist
abi.tar.gz
abi
tests/*.vgr
libmultipath/nvme-ioctl.c
libmultipath/nvme-ioctl.h
+*/*-nv.version
+reference-abi
LIB_BUILDDIRS := \
libmpathcmd \
+ libmpathutil \
libmultipath \
libmpathpersist \
libmpathvalid
BUILDDIRS.clean := $(BUILDDIRS:=.clean) tests.clean
-.PHONY: $(BUILDDIRS) $(BUILDDIRS:=.uninstall) $(BUILDDIRS:=.install) $(BUILDDIRS:=.clean) $(LIB_BUILDDIRS:=.abi)
+.PHONY: $(BUILDDIRS)
all: $(BUILDDIRS)
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"; \
$(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
$(BUILDDIRS.clean):
$(MAKE) -C ${@:.clean=} clean
-$(BUILDDIRS:=.install):
+%.install: %
$(MAKE) -C ${@:.install=} install
$(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
#
# 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
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
+++ /dev/null
-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".
multipath-tools for Linux
=========================
-
https://github.com/opensvc/multipath-tools
This package provides the following binaries to drive the Device Mapper multipathing driver:
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
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`
* 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.
__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);
else
echo "DM_TYPE=raid"
fi
-if [[ $dmserial ]]; then
+if [ -n "$dmserial" ]; then
echo "DM_SERIAL=$dmserial"
fi
$(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:
#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
/*
* 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).
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
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));
--- /dev/null
+#
+# 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)
--- /dev/null
+#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)))
+{}
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/* 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
--- /dev/null
+#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
/* local includes */
#include "vector.h"
-#include "config.h"
struct strbuf;
+struct config;
/* Global definitions */
#define EOB "}"
/*
* Copyright (c) 2021 SUSE LLC
- * SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <inttypes.h>
#include <stdint.h>
/*
* Copyright (c) 2021 SUSE LLC
- * SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef _STRBUF_H
#define _STRBUF_H
*
* 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 ("").
+ * 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);
* @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
+ * 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.
#include <unistd.h>
#include <errno.h>
#include <libudev.h>
-#include <mpath_persist.h>
#include "util.h"
#include "debug.h"
int systemd_service_enabled_in(const char *dev, const char *prefix)
{
- char path[PATH_SIZE], file[PATH_MAX], service[PATH_SIZE];
+ static const char service[] = "multipathd.service";
+ char path[PATH_MAX], file[PATH_MAX];
DIR *dirfd;
struct dirent *d;
int found = 0;
- snprintf(service, PATH_SIZE, "multipathd.service");
- snprintf(path, PATH_SIZE, "%s/systemd/system", prefix);
+ if (safe_sprintf(path, "%s/systemd/system", prefix))
+ return 0;
+
condlog(3, "%s: checking for %s in %s", dev, service, path);
dirfd = opendir(path);
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) {
free(res->di);
}
-void close_fd(void *arg)
-{
- close((long)arg);
-}
-
void cleanup_free_ptr(void *arg)
{
void **p = arg;
- if (p && *p)
+ 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)
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 pthread_cleanup_push_cast(f, arg) \
pthread_cleanup_push(((void (*)(void *))&f), (arg))
-void close_fd(void *arg);
+void cleanup_fd_ptr(void *arg);
void cleanup_free_ptr(void *arg);
void cleanup_mutex(void *arg);
#include <stdlib.h>
#include "vector.h"
+#include "msort.h"
/*
* Initialize vector struct.
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);
+
+}
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
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
#ifndef LIB_MPATH_VALID_H
#define LIB_MPATH_VALID_H
-#ifdef __cpluscplus
+#ifdef __cplusplus
extern "C" {
#endif
* 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
* 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
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)
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)
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)
const Bindings *bindings)
{
int rc;
- long fd;
+ int fd = -1;
char tempname[PATH_MAX];
mode_t old_umask;
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) {
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
*
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, "
}
/* 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);
#
# 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= \
#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
/* 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)
#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"
#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
#include "checkers.h"
#include "debug.h"
-#include "../libmultipath/sg_include.h"
+#include "sg_include.h"
#define INQUIRY_CMDLEN 6
#define INQUIRY_CMD 0x12
#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
}
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)))
{
}
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)
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;
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);
#include <urcu.h>
#include <inttypes.h>
#include "byteorder.h"
+#include "globals.h"
#define ORIGIN_DEFAULT 0
#define ORIGIN_CONFIG 1
* 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;
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,
* 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;
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);
}
*/
const char *action = is_mpath ? "change" : "add";
const char *env;
+ ssize_t len, ret;
if (!pp->udev)
return;
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));
}
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;
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;
}
}
}
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;
}
}
}
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);
#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,
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)
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 */
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
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);
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);
/*
* 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;
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;
}
}
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:
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);
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;
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) {
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) {
}
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
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) {
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
/* 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);
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));
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)
{
const char *dev_t;
char sys_path[64];
- long fd;
+ int fd = -1;
int rc;
if (map->ana_supported != YNU_UNDEF)
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__,
}
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.
*/
{
/* Generic NVMe */
- .vendor = "NVME",
+ .vendor = "NVM[eE]",
.product = ".*",
.uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
.checker_name = NONE,
.no_path_retry = (300 / DEFAULT_CHECKINT),
.checker_name = EMC_CLARIION,
.prio_name = PRIO_EMC,
+ .detect_checker = DETECT_CHECKER_OFF,
},
{
/* Invista / VPLEX */
},
{
/* OceanStor NVMe */
- .vendor = "NVME",
+ .vendor = "NVM[eE]",
.product = "Huawei-XSG1",
.checker_name = DIRECTIO,
.no_path_retry = 12,
* The new version inherits the previous ones.
*/
-LIBMULTIPATH_15.0.0 {
+LIBMULTIPATH_16.0.0 {
global:
/* symbols referenced by multipath and multipathd */
add_foreign;
alloc_path;
alloc_path_layout;
alloc_path_with_pathinfo;
- alloc_strvec;
- append_strbuf_str;
change_foreign;
check_alias_settings;
check_daemon;
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;
find_mpe;
find_path_by_dev;
find_path_by_devt;
- find_slot;
foreign_multipath_layout;
foreign_path_layout;
free_config;
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;
init_prio;
io_err_stat_handle_pathfail;
is_path_valid;
- is_quote;
libmp_dm_task_create;
libmp_get_version;
libmp_get_multipath_config;
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;
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;
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;
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;
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 */
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;
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:
*;
#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)
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);
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,
};
* 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)
{
#
# 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 = \
#include "../structs.h"
#include "../prio.h"
#include "../discovery.h"
-#include "../debug.h"
+#include "debug.h"
#include "alua_rtpg.h"
#define SENSE_BUFF_LEN 32
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)
{
}
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;
*-----------------------------------------------------------------------------
*/
#define CODESET_BINARY 0x1
-#define CODESET_ACSII 0x2
+#define CODESET_ASCII 0x2
#define CODESET_UTF8 0x3
#define ASSOCIATION_UNIT 0x0
/*
* (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
* 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
*
* 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:
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))
}
/*
- * 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,
#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];
"# 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,
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;
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)
*/
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)";
#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
int fast_io_fail;
unsigned int dev_loss;
int eh_deadline;
+ bool is_checked;
/* configlet pointers */
vector hwe;
struct gen_path generic_path;
int add_feature (char **, const char *);
int remove_feature (char **, const char *);
-extern char sysfs_path[PATH_SIZE];
-
#endif /* _STRUCTS_H */
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)) {
* 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;
}
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;
}
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;
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;
}
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];
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)) {
#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);
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);
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)) {
* 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.
*/
#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"
replace_wwids(vector mp)
{
int i, can_write;
- long fd;
+ int fd = -1;
struct multipath * mpp;
size_t len;
int ret = -1;
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;
int
remove_wwid(char *wwid) {
- long fd;
+ int fd = -1;
int len, can_write;
char *str;
int ret = -1;
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");
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
}
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);
}
#
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
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);
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)
return FIND_MULTIPATHS_ERROR;
};
- pthread_cleanup_push(close_fd, (void *)fd);
/*
* We just created the file. Set st_mtim to our desired
* expiry time.
path, strerror(errno));
}
r = fstat(fd, &st);
- pthread_cleanup_pop(1);
+ close(fd);
} else
return FIND_MULTIPATHS_NEVER;
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);
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;
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;
.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)
.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)
(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.
.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
.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.
.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
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
\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
\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
\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
.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)
#
# 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"
#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)
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
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)
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))
--- /dev/null
+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));
+}
#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)
{
return 0;
}
-static struct key *
-find_key (const char * str)
+struct key *find_key (const char * str)
{
int i;
int len, klen;
return r;
}
-static uint64_t
-fingerprint(const struct _vector *vec)
+uint64_t fingerprint(const struct _vector *vec)
{
int i;
uint64_t fp = 0;
if (alloc_handlers())
return 1;
+ init_handler_callbacks();
return 0;
}
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);
-}
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_ */
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)
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;
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"
/*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*/
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;
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;
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)
{
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);
}
}
* 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 {
{
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
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;
}
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, ¶ms, vecs)) {
condlog(0, "%s: failed to setup map for"
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;
}
/* Tell main thread that thread has started */
post_config_state(DAEMON_CONFIGURE);
- init_handler_callbacks();
umask(077);
/*
}
static void
-defered_failback_tick (vector mpvec)
+deferred_failback_tick (vector mpvec)
{
struct multipath * mpp;
unsigned int i;
--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);
}
}
}
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).
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",
}
return 1;
}
+enum checker_state {
+ CHECKER_STARTING,
+ CHECKER_RUNNING,
+ CHECKER_FINISHED,
+};
static void *
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;
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);
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) {
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,
if (!vecs)
return NULL;
- pthread_mutex_init(&vecs->lock.mutex, NULL);
+ init_lock(&vecs->lock);
return vecs;
}
extern char *optarg;
extern int optind;
int arg;
- int err;
+ int err = 0;
int foreground = 0;
struct config *conf;
char *opt_k_arg = NULL;
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;
}
--- /dev/null
+.\" ----------------------------------------------------------------------------
+.\" 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
--- /dev/null
+/*
+ * 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"
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATHD 8 2016-10-27 Linux
+.TH MULTIPATHD 8 2022-09-03 Linux
.
.
.\" ----------------------------------------------------------------------------
.\" ----------------------------------------------------------------------------
.
.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
.\" ----------------------------------------------------------------------------
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).
.
.
.\" ----------------------------------------------------------------------------
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
.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).
.
.
.\" ----------------------------------------------------------------------------
* 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;
{
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;
}
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)
{
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)
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;
/* 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);
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;
#include <stdbool.h>
+bool waiting_clients(void);
void uxsock_cleanup(void *arg);
void *uxsock_listen(long ux_sock,
void * trigger_data);
#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)
{
(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);
}
/*
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 */
}
|| 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)
# 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)
@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))
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}};
#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;
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;
{ "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);
}
/*
- * Some macros to avoid boilerplace code
+ * Some macros to avoid boilerplate code
*/
#define CHECK_STATE(state) ({ \
/***** 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)
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" };
/* 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);
}
--- /dev/null
+/*
+ * 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;
+}
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) {
#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, ...)
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);
}
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),
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),
/* 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. */
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
/* 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.