From 1ff3a804f63b56d0878adf894370b36890e560ff Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 14 Jan 2022 13:50:20 +0900 Subject: [PATCH] Imported Upstream version 0.8.8 --- .github/workflows/abi.yaml | 54 ++ .github/workflows/coverity.yaml | 51 ++ .github/workflows/native.yaml | 62 ++- .gitignore | 5 + .mailmap | 31 ++ Makefile | 61 ++- Makefile.inc | 7 + kpartx/kpartx.c | 4 +- kpartx/lopart.c | 102 ++-- kpartx/lopart.h | 3 +- libdmmp/Makefile | 8 +- libmpathcmd/Makefile | 13 +- libmpathpersist/Makefile | 15 +- libmpathpersist/libmpathpersist.version | 12 +- libmpathpersist/mpath_persist.c | 4 +- libmpathpersist/mpath_updatepr.c | 1 - libmpathvalid/Makefile | 16 +- libmultipath/Makefile | 15 +- libmultipath/alias.c | 6 +- libmultipath/blacklist.c | 27 +- libmultipath/checkers.c | 4 +- libmultipath/checkers.h | 4 +- libmultipath/checkers/emc_clariion.c | 3 +- libmultipath/config.c | 85 ++- libmultipath/config.h | 1 - libmultipath/configure.c | 88 +--- libmultipath/defaults.c | 1 - libmultipath/defaults.h | 1 + libmultipath/devmapper.c | 40 +- libmultipath/dict.c | 551 +++++++++++++------- libmultipath/discovery.c | 259 +++++----- libmultipath/discovery.h | 2 + libmultipath/dmparser.c | 47 +- libmultipath/foreign.c | 74 ++- libmultipath/foreign.h | 23 +- libmultipath/foreign/nvme.c | 4 +- libmultipath/generic.h | 8 + libmultipath/hwtable.c | 26 +- libmultipath/io_err_stat.c | 12 +- libmultipath/libmultipath.version | 90 ++-- libmultipath/lock.c | 12 +- libmultipath/lock.h | 11 +- libmultipath/log.c | 23 +- libmultipath/log_pthread.c | 2 - libmultipath/memory.c | 444 ---------------- libmultipath/memory.h | 66 --- libmultipath/parser.c | 56 +- libmultipath/parser.h | 15 +- libmultipath/pgpolicies.c | 5 +- libmultipath/print.c | 409 +++++++++------ libmultipath/print.h | 60 +-- libmultipath/prio.c | 4 +- libmultipath/prio.h | 1 - libmultipath/prioritizers/alua_rtpg.c | 13 +- libmultipath/prioritizers/alua_spc3.h | 43 +- libmultipath/prioritizers/path_latency.c | 6 +- libmultipath/prioritizers/weightedpath.c | 5 +- libmultipath/propsel.c | 13 +- libmultipath/strbuf.c | 5 + libmultipath/strbuf.h | 14 + libmultipath/structs.c | 51 +- libmultipath/structs.h | 9 + libmultipath/structs_vec.c | 56 +- libmultipath/structs_vec.h | 6 +- libmultipath/sysfs.c | 2 +- libmultipath/time-util.c | 12 + libmultipath/time-util.h | 1 + libmultipath/uevent.c | 58 ++- libmultipath/util.c | 21 +- libmultipath/util.h | 2 + libmultipath/uxsock.c | 10 +- libmultipath/uxsock.h | 6 - libmultipath/vector.c | 15 +- libmultipath/version.h | 4 +- mpathpersist/mpathpersist.8 | 4 +- multipath/main.c | 42 +- multipath/multipath.8 | 2 +- multipath/multipath.conf.5 | 17 +- multipathd/cli.c | 204 ++------ multipathd/cli.h | 104 ++-- multipathd/cli_handlers.c | 630 ++++++++++++----------- multipathd/cli_handlers.h | 61 +-- multipathd/main.c | 485 +++++++++-------- multipathd/main.h | 4 +- multipathd/multipathd.8 | 14 +- multipathd/multipathd.service | 5 +- multipathd/multipathd.socket | 3 + multipathd/uxclnt.c | 5 +- multipathd/uxlsnr.c | 542 ++++++++++++++----- multipathd/uxlsnr.h | 4 +- multipathd/waiter.c | 6 +- tests/pgpolicy.c | 1 + tests/uevent.c | 8 +- tests/vpd.c | 144 +++++- 94 files changed, 3007 insertions(+), 2558 deletions(-) create mode 100644 .github/workflows/abi.yaml create mode 100644 .github/workflows/coverity.yaml create mode 100644 .mailmap delete mode 100644 libmultipath/memory.c delete mode 100644 libmultipath/memory.h diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml new file mode 100644 index 0000000..0a40104 --- /dev/null +++ b/.github/workflows/abi.yaml @@ -0,0 +1,54 @@ +name: check-abi +on: + - push + - pull_request +env: + ABI_BRANCH: ${{ secrets.ABI_BRANCH }} + +jobs: + save-and-test-ABI: + runs-on: ubuntu-20.04 + steps: + - name: set ABI branch + if: ${{ env.ABI_BRANCH == '' }} + run: echo "ABI_BRANCH=master" >> $GITHUB_ENV + - name: checkout + uses: actions/checkout@v2 + - name: get reference ABI + id: reference + continue-on-error: true + uses: dawidd6/action-download-artifact@v2 + with: + workflow: abi.yaml + branch: ${{ env.ABI_BRANCH }} + name: abi + path: reference-abi + - name: update + run: sudo apt-get update + - name: dependencies + run: > + 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 + - name: create ABI + run: make -O -j$(grep -c ^processor /proc/cpuinfo) abi.tar.gz + - name: save ABI + uses: actions/upload-artifact@v1 + with: + name: abi + path: abi + - name: compare ABI against reference + id: compare + continue-on-error: true + if: ${{ steps.reference.outcome == 'success' }} + run: make abi-test + - name: save differences + if: ${{ steps.compare.outcome == 'failure' }} + uses: actions/upload-artifact@v1 + with: + name: abi-test + path: abi-test + - name: fail + if: ${{ env.ABI_BRANCH != github.ref_name && steps.compare.outcome == 'failure' }} + run: false diff --git a/.github/workflows/coverity.yaml b/.github/workflows/coverity.yaml new file mode 100644 index 0000000..a8b56d4 --- /dev/null +++ b/.github/workflows/coverity.yaml @@ -0,0 +1,51 @@ +name: coverity +on: + push: + branches: + - coverity + +jobs: + upload-coverity-scan: + runs-on: ubuntu-20.04 + steps: + - name: checkout + uses: actions/checkout@v2 + - name: dependencies + run: > + 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 + - name: download coverity + run: > + curl -o cov-analysis-linux64.tar.gz + --form token="$COV_TOKEN" + --form project="$COV_PROJECT" + https://scan.coverity.com/download/cxx/linux64 + env: + COV_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COV_PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} + - name: unpack coverity + run: | + mkdir -p coverity + tar xfz cov-analysis-linux64.tar.gz --strip 1 -C coverity + - name: build with cov-build + run: > + PATH="$PWD/coverity/bin:$PATH" + cov-build --dir cov-int make -O -j"$(grep -c ^processor /proc/cpuinfo)" + - name: pack results + run: tar cfz multipath-tools.tgz cov-int + - name: submit results + run: > + curl + --form token="$COV_TOKEN" + --form email="$COV_EMAIL" + --form file="@multipath-tools.tgz" + --form version="${{ github.ref_name }}" + --form description="$(git describe --tags --match "0.*")" + --form project="$COV_PROJECT" + https://scan.coverity.com/builds + env: + COV_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COV_PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} + COV_EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 2bb1886..19c9e29 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -8,15 +8,17 @@ on: pull_request: jobs: - build-and-test: + stable: runs-on: ubuntu-20.04 strategy: matrix: - os: [buster, jessie, sid, alpine, fedora-34] + os: [buster, jessie, bullseye, fedora-35] arch: ['', '-i386'] exclude: - os: fedora-34 arch: '-i386' + - os: fedora-35 + arch: '-i386' container: mwilck/multipath-build-${{ matrix.os }}${{ matrix.arch }} steps: - name: checkout @@ -30,3 +32,59 @@ jobs: CC: clang run: make test + rolling: + runs-on: ubuntu-20.04 + strategy: + matrix: + os: ['debian:sid', 'alpine', 'fedora:rawhide'] + arch: ['amd64', 'i386'] + exclude: + - os: 'fedora:rawhide' + arch: 'i386' + container: ${{ matrix.arch }}/${{ matrix.os }} + steps: + - name: update + if: ${{ matrix.os == 'debian:sid' }} + run: apt-get update + - name: dependencies-debian + if: ${{ matrix.os == 'debian:sid' }} + run: > + apt-get install --yes -o APT::Immediate-Configure=0 + gcc clang make pkg-config + libdevmapper-dev + libreadline-dev + libaio-dev + libudev-dev + libjson-c-dev + liburcu-dev + libcmocka-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 + - name: dependencies-fedora + if: ${{ matrix.os == 'fedora:rawhide' }} + run: > + dnf install -y + make clang gcc pkgconfig + libaio-devel + device-mapper-devel + libselinux-devel + libsepol-devel + readline-devel + ncurses-devel + userspace-rcu-devel + json-c-devel + libcmocka-devel + - name: checkout + uses: actions/checkout@v1 + - name: build and test + run: make test + - name: clean + run: make clean + - name: clang + env: + CC: clang + run: make test diff --git a/.gitignore b/.gitignore index 087dffc..8e09f95 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *~ *.so *.so.0 +*.abi *.a *.gz *.d @@ -13,6 +14,10 @@ kpartx/kpartx multipath/multipath multipathd/multipathd mpathpersist/mpathpersist +abi.tar.gz +abi +abi-test +compile_commands.json .nfs* *.swp *.patch diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..14996ab --- /dev/null +++ b/.mailmap @@ -0,0 +1,31 @@ +# +# This list is used by git-shortlog to fix a few botched name translations +# in the git archive, either because the author's full name was messed up +# and/or not always written the same way, making contributions from the +# same person appearing not to be so or badly displayed. Also allows for +# old email addresses to map to new email addresses. +# +# For format details, see "MAPPING AUTHORS" in "man git-shortlog". +# +# Please keep this list dictionary sorted. +# +Bart Van Assche +Bart Van Assche +Benjamin Marzinski +Benjamin Marzinski +Benjamin Marzinski bmarzins@sourceware.org +Chongyun Wu Wuchongyun +Chongyun Wu +Christophe Varoqui +Christophe Varoqui +Christophe Varoqui +Christophe Varoqui +Christophe Varoqui root +Christophe Varoqui root +Christophe Varoqui root +Christophe Varoqui +Hannes Reinecke +Hannes Reinecke +Martin Wilck +Martin Wilck +wei huang wei.huang diff --git a/Makefile b/Makefile index 7f21db8..82e0ea3 100644 --- a/Makefile +++ b/Makefile @@ -2,33 +2,76 @@ # Copyright (C) 2003 Christophe Varoqui, # -BUILDDIRS := \ +LIB_BUILDDIRS := \ libmpathcmd \ libmultipath \ + libmpathpersist \ + libmpathvalid + +ifneq ($(ENABLE_LIBDMMP),0) +LIB_BUILDDIRS += \ + libdmmp +endif + +BUILDDIRS := $(LIB_BUILDDIRS) \ libmultipath/prioritizers \ libmultipath/checkers \ libmultipath/foreign \ - libmpathpersist \ - libmpathvalid \ multipath \ multipathd \ mpathpersist \ kpartx -ifneq ($(ENABLE_LIBDMMP),0) -BUILDDIRS += \ - libdmmp -endif BUILDDIRS.clean := $(BUILDDIRS:=.clean) tests.clean -.PHONY: $(BUILDDIRS) $(BUILDDIRS:=.uninstall) $(BUILDDIRS:=.install) $(BUILDDIRS.clean) +.PHONY: $(BUILDDIRS) $(BUILDDIRS:=.uninstall) $(BUILDDIRS:=.install) $(BUILDDIRS:=.clean) $(LIB_BUILDDIRS:=.abi) all: $(BUILDDIRS) $(BUILDDIRS): $(MAKE) -C $@ +$(LIB_BUILDDIRS:=.abi): $(LIB_BUILDDIRS) + $(MAKE) -C ${@:.abi=} abi + +# Create formal representation of the ABI +# Useful for verifying ABI compatibility +# Requires abidw from the abigail suite (https://sourceware.org/libabigail/) +.PHONY: abi +abi: $(LIB_BUILDDIRS:=.abi) + mkdir -p $@ + ln -ft $@ $(LIB_BUILDDIRS:=/*.abi) + +abi.tar.gz: abi + tar cfz $@ abi + +# Check the ABI against a reference. +# This requires the ABI from a previous run to be present +# in the directory "reference-abi" +# Requires abidiff from the abigail suite +abi-test: abi reference-abi $(wildcard abi/*.abi) + @err=0; \ + for lib in abi/*.abi; do \ + diff=$$(abidiff "reference-$$lib" "$$lib") || { \ + err=1; \ + echo "==== ABI differences in for $$lib ===="; \ + echo "$$diff"; \ + }; \ + done >$@; \ + if [ $$err -eq 0 ]; then \ + echo "*** OK, ABI unchanged ***"; \ + else \ + echo "*** WARNING: ABI has changed, see file $@ ***"; \ + fi; \ + [ $$err -eq 0 ] + +# Create compile_commands.json, useful for using clangd with an IDE +# Requires bear (https://github.com/rizsotto/Bear) +compile_commands.json: Makefile Makefile.inc $(BUILDDIRS:=/Makefile) + $(MAKE) clean + bear -- $(MAKE) + libmultipath libdmmp: libmpathcmd libmpathpersist libmpathvalid multipath multipathd: libmultipath libmultipath/prioritizers libmultipath/checkers libmultipath/foreign: libmultipath @@ -48,6 +91,8 @@ $(BUILDDIRS:=.uninstall): $(MAKE) -C ${@:.uninstall=} uninstall clean: $(BUILDDIRS.clean) + rm -rf abi abi.tar.gz abi-test compile_commands.json + install: all $(BUILDDIRS:=.install) uninstall: $(BUILDDIRS:=.uninstall) diff --git a/Makefile.inc b/Makefile.inc index d0ec9b4..b340f2a 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -79,6 +79,7 @@ GZIP = gzip -9 -c RM = rm -f LN = ln -sf INSTALL_PROGRAM = install +NV_VERSION_SCRIPT = $(VERSION_SCRIPT:%.version=%-nv.version) # $(call TEST_CC_OPTION,option,fallback) # Test if the C compiler supports the option. @@ -140,3 +141,9 @@ check_file = $(shell \ %.o: %.c @echo building $@ because of $? $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +%.abi: %.so.0 + abidw $< >$@ + +%.abi: %.so + abidw $< >$@ diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 7bc6454..3c49999 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -359,9 +359,7 @@ main(int argc, char **argv){ exit (0); if (!loopdev) { - loopdev = find_unused_loop_device(); - - if (set_loop(loopdev, rpath, 0, &ro)) { + if (set_loop(&loopdev, rpath, 0, &ro)) { fprintf(stderr, "can't set up loop\n"); exit (1); } diff --git a/kpartx/lopart.c b/kpartx/lopart.c index 9b65255..512a59f 100644 --- a/kpartx/lopart.c +++ b/kpartx/lopart.c @@ -39,24 +39,6 @@ #define LOOP_CTL_GET_FREE 0x4C82 #endif -static char * -xstrdup (const char *s) -{ - char *t; - - if (s == NULL) - return NULL; - - t = strdup (s); - - if (t == NULL) { - fprintf(stderr, "not enough memory"); - exit(1); - } - - return t; -} - #define SIZE(a) (sizeof(a)/sizeof(a[0])) char *find_loop_by_file(const char *filename) @@ -157,51 +139,59 @@ char *find_loop_by_file(const char *filename) return found; } -char *find_unused_loop_device(void) +static char *find_unused_loop_device(int mode, int *loop_fd) { - char dev[20], *next_loop_dev = NULL; + char dev[21]; int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0; + int next_loop_fd; struct stat statbuf; struct loop_info loopinfo; FILE *procdev; - while (next_loop_dev == NULL) { - if (stat("/dev/loop-control", &statbuf) == 0 && - S_ISCHR(statbuf.st_mode)) { - int next_loop_fd; - - next_loop_fd = open("/dev/loop-control", O_RDWR); - if (next_loop_fd < 0) - return NULL; - next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE); - close(next_loop_fd); - if (next_loop < 0) - return NULL; - } + next_loop_fd = open("/dev/loop-control", O_RDWR); + if (next_loop_fd < 0) + goto no_loop_fd; + + if (!(fstat(next_loop_fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode))) + goto nothing_found; + + for (;;) { + next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE); + if (next_loop < 0) + goto nothing_found; sprintf(dev, "/dev/loop%d", next_loop); - fd = open (dev, O_RDONLY); + fd = open (dev, mode); if (fd >= 0) { if (fstat (fd, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { somedev++; if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0) someloop++; /* in use */ - else if (errno == ENXIO) - next_loop_dev = xstrdup(dev); + else if (errno == ENXIO) { + char *name = strdup(dev); + + if (name == NULL) + close(fd); + else + *loop_fd = fd; + close(next_loop_fd); + return name; + } } close (fd); /* continue trying as long as devices exist */ - continue; - } - break; + } else + break; } - if (next_loop_dev) - return next_loop_dev; +nothing_found: + close(next_loop_fd); + +no_loop_fd: /* Nothing found. Why not? */ if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) { char line[100]; @@ -220,33 +210,33 @@ char *find_unused_loop_device(void) } if (!somedev) - fprintf(stderr, "mount: could not find any device /dev/loop#"); + fprintf(stderr, "mount: could not find any device /dev/loop#\n"); else if (!someloop) { if (loop_known == 1) fprintf(stderr, "mount: Could not find any loop device.\n" - " Maybe /dev/loop# has a wrong major number?"); + " Maybe /dev/loop# has a wrong major number?\n"); else if (loop_known == -1) fprintf(stderr, "mount: Could not find any loop device, and, according to %s,\n" " this kernel does not know about the loop device.\n" - " (If so, then recompile or `modprobe loop'.)", + " (If so, then recompile or `modprobe loop'.)\n", PROC_DEVICES); else fprintf(stderr, "mount: Could not find any loop device. Maybe this kernel does not know\n" " about the loop device (then recompile or `modprobe loop'), or\n" - " maybe /dev/loop# has the wrong major number?"); + " maybe /dev/loop# has the wrong major number?\n"); } else - fprintf(stderr, "mount: could not find any free loop device"); + fprintf(stderr, "mount: could not find any free loop device\n"); return NULL; } -int set_loop(const char *device, const char *file, int offset, int *loopro) +int set_loop(char **device, const char *file, int offset, int *loopro) { struct loop_info loopinfo; - int fd, ffd, mode; + int fd = -1, ret = 1, ffd, mode; mode = (*loopro ? O_RDONLY : O_RDWR); @@ -261,9 +251,9 @@ int set_loop(const char *device, const char *file, int offset, int *loopro) } } - if ((fd = open (device, mode)) < 0) { + *device = find_unused_loop_device(mode, &fd); + if (!*device) { close(ffd); - perror (device); return 1; } @@ -277,22 +267,20 @@ int set_loop(const char *device, const char *file, int offset, int *loopro) if (ioctl(fd, LOOP_SET_FD, (void*)(uintptr_t)(ffd)) < 0) { perror ("ioctl: LOOP_SET_FD"); - close (fd); - close (ffd); - return 1; + goto out; } if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) { (void) ioctl (fd, LOOP_CLR_FD, 0); perror ("ioctl: LOOP_SET_STATUS"); - close (fd); - close (ffd); - return 1; + goto out; } + ret = 0; +out: close (fd); close (ffd); - return 0; + return ret; } int del_loop(const char *device) diff --git a/kpartx/lopart.h b/kpartx/lopart.h index d3bad10..c73ab23 100644 --- a/kpartx/lopart.h +++ b/kpartx/lopart.h @@ -1,5 +1,4 @@ extern int verbose; -extern int set_loop (const char *, const char *, int, int *); +extern int set_loop (char **, const char *, int, int *); extern int del_loop (const char *); -extern char * find_unused_loop_device (void); extern char * find_loop_by_file (const char *); diff --git a/libdmmp/Makefile b/libdmmp/Makefile index 79b92fb..de61668 100644 --- a/libdmmp/Makefile +++ b/libdmmp/Makefile @@ -25,7 +25,11 @@ all: $(LIBS) doc $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) - $(LN) $@ $(DEVLIB) + +$(DEVLIB): $(LIBS) + $(LN) $(LIBS) $@ + +abi: $(DEVLIB:%.so=%.abi) install: doc.gz mkdir -p $(DESTDIR)$(usrlibdir) @@ -54,7 +58,7 @@ uninstall: $(RM) $(DESTDIR)$(pkgconfdir)/$(PKGFILE) clean: dep_clean - $(RM) core *.a *.o *.gz *.so *.so.* + $(RM) core *.a *.o *.gz *.so *.so.* *.abi $(NV_VERSION_SCRIPT) $(RM) docs/man/*.gz $(MAKE) -C test clean diff --git a/libmpathcmd/Makefile b/libmpathcmd/Makefile index 2591019..72cab1e 100644 --- a/libmpathcmd/Makefile +++ b/libmpathcmd/Makefile @@ -15,6 +15,17 @@ $(LIBS): $(OBJS) $(VERSION_SCRIPT) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \ -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS) +$(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) + $(DEVLIB): $(LIBS) $(LN) $(LIBS) $@ @@ -31,7 +42,7 @@ uninstall: $(RM) $(DESTDIR)$(includedir)/mpath_cmd.h clean: dep_clean - $(RM) core *.a *.o *.so *.so.* *.gz + $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT) include $(wildcard $(OBJS:.o=.d)) diff --git a/libmpathpersist/Makefile b/libmpathpersist/Makefile index 57103e5..1e6399d 100644 --- a/libmpathpersist/Makefile +++ b/libmpathpersist/Makefile @@ -3,7 +3,7 @@ include ../Makefile.inc SONAME = 0 DEVLIB = libmpathpersist.so LIBS = $(DEVLIB).$(SONAME) -VERSION_SCRIPT := libmpathpersist.version +VERSION_SCRIPT:= libmpathpersist.version CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) LDFLAGS += -L$(multipathdir) -L$(mpathcmddir) @@ -18,6 +18,17 @@ $(LIBS): $(OBJS) $(VERSION_SCRIPT) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \ -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS) +$(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) + $(DEVLIB): $(LIBS) $(LN) $(LIBS) $@ @@ -44,7 +55,7 @@ uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) clean: dep_clean - $(RM) core *.a *.o *.so *.so.* *.gz + $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT) include $(wildcard $(OBJS:.o=.d)) diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version index e074813..fa312f6 100644 --- a/libmpathpersist/libmpathpersist.version +++ b/libmpathpersist/libmpathpersist.version @@ -10,7 +10,7 @@ * * See libmultipath.version for general policy about version numbers. */ -LIBMPATHPERSIST_1.0.0 { +LIBMPATHPERSIST_2.0.0 { global: __mpath_persistent_reserve_in; @@ -28,11 +28,9 @@ global: prout_do_scsi_ioctl; update_map_pr; -local: *; -}; - -LIBMPATHPERSIST_1.1.0 { -global: + /* added in 1.1.0 */ libmpathpersist_init; libmpathpersist_exit; -} LIBMPATHPERSIST_1.0.0; + +local: *; +}; diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 803a2a2..3097c81 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -239,7 +239,7 @@ static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias, alias = NULL; } out: - FREE(alias); + free(alias); return ret; } @@ -342,7 +342,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd, update_prkey(alias, 0); } out1: - FREE(alias); + free(alias); return ret; } diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c index 0aca28e..bdecaa0 100644 --- a/libmpathpersist/mpath_updatepr.c +++ b/libmpathpersist/mpath_updatepr.c @@ -14,7 +14,6 @@ #include "debug.h" #include "mpath_cmd.h" #include "uxsock.h" -#include "memory.h" #include "mpathpr.h" diff --git a/libmpathvalid/Makefile b/libmpathvalid/Makefile index 6bea4bc..dce2610 100644 --- a/libmpathvalid/Makefile +++ b/libmpathvalid/Makefile @@ -15,9 +15,21 @@ OBJS = mpath_valid.o all: $(LIBS) $(LIBS): $(OBJS) $(VERSION_SCRIPT) - $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) -Wl,--version-script=libmpathvalid.version + $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) \ + -Wl,--version-script=$(VERSION_SCRIPT) $(LN) $(LIBS) $(DEVLIB) +$(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: $(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) @@ -31,7 +43,7 @@ uninstall: $(RM) $(DESTDIR)$(includedir)/mpath_valid.h clean: dep_clean - $(RM) core *.a *.o *.so *.so.* *.gz + $(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT) include $(wildcard $(OBJS:.o=.d)) diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 7f3921c..d4af1a5 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -45,7 +45,7 @@ ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0) CFLAGS += -DLIBDM_API_HOLD_CONTROL endif -OBJS = memory.o parser.o vector.o devmapper.o callout.o \ +OBJS = parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ structs.o discovery.o propsel.o dict.o \ pgpolicies.o debug.o defaults.o uevent.o time-util.o \ @@ -81,6 +81,17 @@ $(LIBS): $(OBJS) $(VERSION_SCRIPT) $(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) + ../tests/$(LIBS): $(OBJS) $(VERSION_SCRIPT) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=`basename $@` \ -o $@ $(OBJS) $(LIBDEPS) @@ -99,7 +110,7 @@ uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) clean: dep_clean - $(RM) core *.a *.o *.so *.so.* *.gz nvme-ioctl.c nvme-ioctl.h + $(RM) core *.a *.o *.so *.so.* *.gz *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT) include $(wildcard $(OBJS:.o=.d)) diff --git a/libmultipath/alias.c b/libmultipath/alias.c index ad7e512..87c33af 100644 --- a/libmultipath/alias.c +++ b/libmultipath/alias.c @@ -356,7 +356,7 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old, * allocated correctly */ if (strcmp(buff, wwid) == 0) - alias = STRDUP(alias_old); + alias = strdup(alias_old); else { alias = NULL; condlog(0, "alias %s already bound to wwid %s, cannot reuse", @@ -578,13 +578,17 @@ static int fix_bindings_file(const struct config *conf, int rc; long fd; char tempname[PATH_MAX]; + mode_t old_umask; if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file)) return -1; + /* coverity: SECURE_TEMP */ + old_umask = umask(0077); if ((fd = mkstemp(tempname)) == -1) { condlog(1, "%s: mkstemp: %m", __func__); return -1; } + umask(old_umask); pthread_cleanup_push(close_fd, (void*)fd); rc = write_bindings_file(bindings, fd); pthread_cleanup_pop(1); diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index 4e315c9..8d15d2e 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -5,7 +5,6 @@ #include #include "checkers.h" -#include "memory.h" #include "vector.h" #include "util.h" #include "debug.h" @@ -46,7 +45,7 @@ int store_ble(vector blist, const char *str, int origin) if (!blist) goto out; - ble = MALLOC(sizeof(struct blentry)); + ble = calloc(1, sizeof(struct blentry)); if (!ble) goto out; @@ -63,9 +62,9 @@ int store_ble(vector blist, const char *str, int origin) vector_set_slot(blist, ble); return 0; out1: - FREE(ble); + free(ble); out: - FREE(strdup_str); + free(strdup_str); return 1; } @@ -77,12 +76,12 @@ int alloc_ble_device(vector blist) if (!blist) return 1; - ble = MALLOC(sizeof(struct blentry_device)); + ble = calloc(1, sizeof(struct blentry_device)); if (!ble) return 1; if (!vector_alloc_slot(blist)) { - FREE(ble); + free(ble); return 1; } vector_set_slot(blist, ble); @@ -105,7 +104,7 @@ int set_ble_device(vector blist, const char *vendor, const char *product, int or return 1; if (vendor) { - vendor_str = STRDUP(vendor); + vendor_str = strdup(vendor); if (!vendor_str) goto out; @@ -116,7 +115,7 @@ int set_ble_device(vector blist, const char *vendor, const char *product, int or ble->vendor = vendor_str; } if (product) { - product_str = STRDUP(product); + product_str = strdup(product); if (!product_str) goto out1; @@ -216,7 +215,7 @@ setup_default_blist (struct config * conf) VECTOR_SIZE(conf->blist_device) - 1); if (set_ble_device(conf->blist_device, hwe->vendor, hwe->bl_product, ORIGIN_DEFAULT)) { - FREE(ble); + free(ble); vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1); return 1; } @@ -445,8 +444,8 @@ static void free_ble(struct blentry *ble) if (!ble) return; regfree(&ble->regex); - FREE(ble->str); - FREE(ble); + free(ble->str); + free(ble); } void @@ -488,13 +487,13 @@ static void free_ble_device(struct blentry_device *ble) if (ble) { if (ble->vendor) { regfree(&ble->vendor_reg); - FREE(ble->vendor); + free(ble->vendor); } if (ble->product) { regfree(&ble->product_reg); - FREE(ble->product); + free(ble->product); } - FREE(ble); + free(ble); } } diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 8039c2b..ef346d7 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -55,7 +55,7 @@ static struct checker_class *alloc_checker_class(void) { struct checker_class *c; - c = MALLOC(sizeof(struct checker_class)); + c = calloc(1, sizeof(struct checker_class)); if (c) { INIT_LIST_HEAD(&c->node); uatomic_set(&c->refcount, 1); @@ -96,7 +96,7 @@ void free_checker_class(struct checker_class *c) c->name, dlerror()); } } - FREE(c); + free(c); } void cleanup_checkers (void) diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index 2fd1d1c..f4600ed 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -3,7 +3,6 @@ #include #include "list.h" -#include "memory.h" #include "defaults.h" /* @@ -127,8 +126,7 @@ struct checker { short msgid; /* checker-internal extra status */ void * context; /* store for persistent data */ void ** mpcontext; /* store for persistent data shared - multipath-wide. Use MALLOC if - you want to stuff data in. */ + multipath-wide. */ }; static inline int checker_selected(const struct checker *c) diff --git a/libmultipath/checkers/emc_clariion.c b/libmultipath/checkers/emc_clariion.c index 5cd63ac..aa636e7 100644 --- a/libmultipath/checkers/emc_clariion.c +++ b/libmultipath/checkers/emc_clariion.c @@ -15,7 +15,6 @@ #include "libsg.h" #include "checkers.h" #include "debug.h" -#include "memory.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 @@ -102,7 +101,7 @@ int libcheck_init (struct checker * c) /* * Allocate and initialize the path specific context. */ - c->context = MALLOC(sizeof(struct emc_clariion_checker_path_context)); + c->context = calloc(1, sizeof(struct emc_clariion_checker_path_context)); if (!c->context) return 1; ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; diff --git a/libmultipath/config.c b/libmultipath/config.c index 30046a1..c595e76 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -11,7 +11,6 @@ #include #include "checkers.h" -#include "memory.h" #include "util.h" #include "debug.h" #include "parser.h" @@ -245,45 +244,45 @@ free_hwe (struct hwentry * hwe) return; if (hwe->vendor) - FREE(hwe->vendor); + free(hwe->vendor); if (hwe->product) - FREE(hwe->product); + free(hwe->product); if (hwe->revision) - FREE(hwe->revision); + free(hwe->revision); if (hwe->getuid) - FREE(hwe->getuid); + free(hwe->getuid); if (hwe->uid_attribute) - FREE(hwe->uid_attribute); + free(hwe->uid_attribute); if (hwe->features) - FREE(hwe->features); + free(hwe->features); if (hwe->hwhandler) - FREE(hwe->hwhandler); + free(hwe->hwhandler); if (hwe->selector) - FREE(hwe->selector); + free(hwe->selector); if (hwe->checker_name) - FREE(hwe->checker_name); + free(hwe->checker_name); if (hwe->prio_name) - FREE(hwe->prio_name); + free(hwe->prio_name); if (hwe->prio_args) - FREE(hwe->prio_args); + free(hwe->prio_args); if (hwe->alias_prefix) - FREE(hwe->alias_prefix); + free(hwe->alias_prefix); if (hwe->bl_product) - FREE(hwe->bl_product); + free(hwe->bl_product); - FREE(hwe); + free(hwe); } void @@ -308,27 +307,27 @@ free_mpe (struct mpentry * mpe) return; if (mpe->wwid) - FREE(mpe->wwid); + free(mpe->wwid); if (mpe->selector) - FREE(mpe->selector); + free(mpe->selector); if (mpe->getuid) - FREE(mpe->getuid); + free(mpe->getuid); if (mpe->uid_attribute) - FREE(mpe->uid_attribute); + free(mpe->uid_attribute); if (mpe->alias) - FREE(mpe->alias); + free(mpe->alias); if (mpe->prio_name) - FREE(mpe->prio_name); + free(mpe->prio_name); if (mpe->prio_args) - FREE(mpe->prio_args); + free(mpe->prio_args); - FREE(mpe); + free(mpe); } void @@ -350,7 +349,7 @@ struct mpentry * alloc_mpe (void) { struct mpentry * mpe = (struct mpentry *) - MALLOC(sizeof(struct mpentry)); + calloc(1, sizeof(struct mpentry)); return mpe; } @@ -359,7 +358,7 @@ struct hwentry * alloc_hwe (void) { struct hwentry * hwe = (struct hwentry *) - MALLOC(sizeof(struct hwentry)); + calloc(1, sizeof(struct hwentry)); return hwe; } @@ -378,7 +377,7 @@ set_param_str(const char * str) if (!len) return NULL; - dst = (char *)MALLOC(len + 1); + dst = (char *)calloc(1, len + 1); if (!dst) return NULL; @@ -652,7 +651,7 @@ restart: static struct config *alloc_config (void) { - return (struct config *)MALLOC(sizeof(struct config)); + return (struct config *)calloc(1, sizeof(struct config)); } static void _uninit_config(struct config *conf) @@ -661,52 +660,52 @@ static void _uninit_config(struct config *conf) conf = &__internal_config; if (conf->multipath_dir) - FREE(conf->multipath_dir); + free(conf->multipath_dir); if (conf->selector) - FREE(conf->selector); + free(conf->selector); if (conf->uid_attribute) - FREE(conf->uid_attribute); + free(conf->uid_attribute); vector_reset(&conf->uid_attrs); if (conf->getuid) - FREE(conf->getuid); + free(conf->getuid); if (conf->features) - FREE(conf->features); + free(conf->features); if (conf->hwhandler) - FREE(conf->hwhandler); + free(conf->hwhandler); if (conf->bindings_file) - FREE(conf->bindings_file); + free(conf->bindings_file); if (conf->wwids_file) - FREE(conf->wwids_file); + free(conf->wwids_file); if (conf->prkeys_file) - FREE(conf->prkeys_file); + free(conf->prkeys_file); if (conf->prio_name) - FREE(conf->prio_name); + free(conf->prio_name); if (conf->alias_prefix) - FREE(conf->alias_prefix); + free(conf->alias_prefix); if (conf->partition_delim) - FREE(conf->partition_delim); + free(conf->partition_delim); if (conf->prio_args) - FREE(conf->prio_args); + free(conf->prio_args); if (conf->checker_name) - FREE(conf->checker_name); + free(conf->checker_name); if (conf->config_dir) - FREE(conf->config_dir); + free(conf->config_dir); if (conf->enable_foreign) - FREE(conf->enable_foreign); + free(conf->enable_foreign); free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); diff --git a/libmultipath/config.h b/libmultipath/config.h index 933fe0d..c73389b 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -178,7 +178,6 @@ struct config { int strict_timing; int retrigger_tries; int retrigger_delay; - int delayed_reconfig; int uev_wait_timeout; int skip_kpartx; int remove_retries; diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 7edb355..6745976 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -19,7 +19,6 @@ #include "checkers.h" #include "vector.h" -#include "memory.h" #include "devmapper.h" #include "defaults.h" #include "structs.h" @@ -44,10 +43,6 @@ #include "sysfs.h" #include "io_err_stat.h" -/* Time in ms to wait for pending checkers in setup_map() */ -#define WAIT_CHECKERS_PENDING_MS 10 -#define WAIT_ALL_CHECKERS_PENDING_MS 90 - /* group paths in pg by host adapter */ int group_by_host_adapter(struct pathgroup *pgp, vector adapters) @@ -261,42 +256,11 @@ int rr_optimize_path_order(struct pathgroup *pgp) return 0; } -static int wait_for_pending_paths(struct multipath *mpp, - struct config *conf, - int n_pending, int goal, int wait_ms) -{ - static const struct timespec millisec = - { .tv_sec = 0, .tv_nsec = 1000*1000 }; - int i, j; - struct path *pp; - struct pathgroup *pgp; - struct timespec ts; - - do { - vector_foreach_slot(mpp->pg, pgp, i) { - vector_foreach_slot(pgp->paths, pp, j) { - if (pp->state != PATH_PENDING) - continue; - pp->state = get_state(pp, conf, - 0, PATH_PENDING); - if (pp->state != PATH_PENDING && - --n_pending <= goal) - return 0; - } - } - ts = millisec; - while (nanosleep(&ts, &ts) != 0 && errno == EINTR) - /* nothing */; - } while (--wait_ms > 0); - - return n_pending; -} - int setup_map(struct multipath *mpp, char **params, struct vectors *vecs) { struct pathgroup * pgp; struct config *conf; - int i, n_paths, marginal_pathgroups; + int i, marginal_pathgroups; char *save_attr; /* @@ -395,7 +359,6 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs) if (marginal_path_check_enabled(mpp)) start_io_err_stat_thread(vecs); - n_paths = VECTOR_SIZE(mpp->paths); /* * assign paths to path groups -- start with no groups and all paths * in mpp->paths @@ -410,31 +373,6 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs) if (group_paths(mpp, marginal_pathgroups)) return 1; - /* - * If async state detection is used, see if pending state checks - * have finished, to get nr_active right. We can't wait until the - * checkers time out, as that may take 30s or more, and we are - * holding the vecs lock. - */ - if (conf->force_sync == 0 && n_paths > 0) { - int n_pending = pathcount(mpp, PATH_PENDING); - - if (n_pending > 0) - n_pending = wait_for_pending_paths( - mpp, conf, n_pending, 0, - WAIT_CHECKERS_PENDING_MS); - /* ALL paths pending - wait some more, but be satisfied - with only some paths finished */ - if (n_pending == n_paths) - n_pending = wait_for_pending_paths( - mpp, conf, n_pending, - n_paths >= 4 ? 2 : 1, - WAIT_ALL_CHECKERS_PENDING_MS); - if (n_pending > 0) - condlog(2, "%s: setting up map with %d/%d path checkers pending", - mpp->alias, n_pending, n_paths); - } - /* * ponders each path group and determine highest prio pg * to switch over (default to first) @@ -715,6 +653,8 @@ void select_action (struct multipath *mpp, const struct _vector *curmp, cmpp = find_mp_by_wwid(curmp, mpp->wwid); cmpp_by_name = find_mp_by_alias(curmp, mpp->alias); + if (mpp->need_reload || (cmpp && cmpp->need_reload)) + force_reload = 1; if (!cmpp_by_name) { if (cmpp) { @@ -751,8 +691,8 @@ void select_action (struct multipath *mpp, const struct _vector *curmp, mpp->wwid, cmpp->alias, mpp->alias, mpp->alias, cmpp_by_name->wwid); /* reset alias to existing alias */ - FREE(mpp->alias); - mpp->alias = STRDUP(cmpp->alias); + free(mpp->alias); + mpp->alias = strdup(cmpp->alias); mpp->action = ACT_IMPOSSIBLE; return; } @@ -803,8 +743,8 @@ void select_action (struct multipath *mpp, const struct _vector *curmp, return; } - cmpp_feat = STRDUP(cmpp->features); - mpp_feat = STRDUP(mpp->features); + cmpp_feat = strdup(cmpp->features); + mpp_feat = strdup(mpp->features); if (cmpp_feat && mpp_feat) { remove_feature(&mpp_feat, "queue_if_no_path"); remove_feature(&mpp_feat, "retain_attached_hw_handler"); @@ -812,13 +752,13 @@ void select_action (struct multipath *mpp, const struct _vector *curmp, remove_feature(&cmpp_feat, "retain_attached_hw_handler"); if (strcmp(mpp_feat, cmpp_feat)) { select_reload_action(mpp, "features change"); - FREE(cmpp_feat); - FREE(mpp_feat); + free(cmpp_feat); + free(mpp_feat); return; } } - FREE(cmpp_feat); - FREE(mpp_feat); + free(cmpp_feat); + free(mpp_feat); if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector, strlen(mpp->selector))) { @@ -1087,7 +1027,7 @@ check_daemon(void) ret = 1; out_free: - FREE(reply); + free(reply); out: mpath_disconnect(fd); return ret; @@ -1098,7 +1038,7 @@ out: * FORCE_RELOAD_NONE: existing maps aren't touched at all * FORCE_RELOAD_YES: all maps are rebuilt from scratch and (re)loaded in DM * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only - * reloaded in DM if there's a difference. This is useful during startup. + * reloaded in DM if there's a difference. This is normally sufficient. */ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, int force_reload, enum mpath_cmds cmd) @@ -1430,7 +1370,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev, } if (refwwid && strlen(refwwid)) { - *wwid = STRDUP(refwwid); + *wwid = strdup(refwwid); return PATHINFO_OK; } diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c index 082640d..aad8357 100644 --- a/libmultipath/defaults.c +++ b/libmultipath/defaults.c @@ -4,6 +4,5 @@ #include #include "defaults.h" -#include "memory.h" const char * const default_partition_delim = DEFAULT_PARTITION_DELIM; diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index c27946c..7d95413 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -1,6 +1,7 @@ #ifndef _DEFAULTS_H #define _DEFAULTS_H #include +#include /* * If you add or modify a value also update multipath/multipath.conf.5 diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index c05dc20..c0eb335 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -21,7 +21,6 @@ #include "vector.h" #include "structs.h" #include "debug.h" -#include "memory.h" #include "devmapper.h" #include "sysfs.h" #include "config.h" @@ -474,7 +473,7 @@ dm_addmap (int task, const char *target, struct multipath *mpp, dm_task_set_ro(dmt); if (task == DM_DEVICE_CREATE) { - prefixed_uuid = MALLOC(UUID_PREFIX_LEN + + prefixed_uuid = calloc(1, UUID_PREFIX_LEN + strlen(mpp->wwid) + 1); if (!prefixed_uuid) { condlog(0, "cannot create prefixed uuid : %s", @@ -517,11 +516,13 @@ dm_addmap (int task, const char *target, struct multipath *mpp, libmp_udev_wait(cookie); freeout: if (prefixed_uuid) - FREE(prefixed_uuid); + free(prefixed_uuid); addout: dm_task_destroy (dmt); + if (r) + mpp->need_reload = false; return r; } @@ -1285,7 +1286,7 @@ struct multipath *dm_get_multipath(const char *name) if (!mpp) return NULL; - mpp->alias = STRDUP(name); + mpp->alias = strdup(name); if (!mpp->alias) goto out; @@ -1293,8 +1294,10 @@ struct multipath *dm_get_multipath(const char *name) if (dm_get_map(name, &mpp->size, NULL) != DMP_OK) goto out; - dm_get_uuid(name, mpp->wwid, WWID_SIZE); - dm_get_info(name, &mpp->dmi); + if (dm_get_uuid(name, mpp->wwid, WWID_SIZE) != 0) + condlog(2, "%s: failed to get uuid for %s", __func__, name); + if (dm_get_info(name, &mpp->dmi) != 0) + condlog(2, "%s: failed to get info for %s", __func__, name); return mpp; out: @@ -1388,7 +1391,6 @@ dm_mapname(int major, int minor) const char *map; struct dm_task *dmt; int r; - int loop = MAX_WAIT * LOOPS_PER_SEC; if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS))) return NULL; @@ -1398,29 +1400,15 @@ dm_mapname(int major, int minor) goto bad; dm_task_no_open_count(dmt); - - /* - * device map might not be ready when we get here from - * daemon uev_trigger -> uev_add_map - */ - while (--loop) { - r = libmp_dm_task_run(dmt); - - if (r) - break; - - usleep(1000 * 1000 / LOOPS_PER_SEC); - } - + r = libmp_dm_task_run(dmt); if (!r) { dm_log_error(2, DM_DEVICE_STATUS, dmt); - condlog(0, "%i:%i: timeout fetching map name", major, minor); goto bad; } map = dm_task_get_name(dmt); if (map && strlen(map)) - response = STRDUP((const char *)map); + response = strdup((const char *)map); dm_task_destroy(dmt); return response; @@ -1598,7 +1586,7 @@ dm_cancel_deferred_remove (struct multipath *mpp __attribute__((unused))) static struct dm_info * alloc_dminfo (void) { - return MALLOC(sizeof(struct dm_info)); + return calloc(1, sizeof(struct dm_info)); } int @@ -1614,7 +1602,7 @@ dm_get_info (const char * mapname, struct dm_info ** dmi) return 1; if (do_get_info(mapname, *dmi) != 0) { - FREE(*dmi); + free(*dmi); *dmi = NULL; return 1; } @@ -1715,7 +1703,7 @@ void dm_reassign_deps(char *table, const char *dep, const char *newdep) n += strlen(newdep); p += strlen(dep); strcat(n, p); - FREE(newtable); + free(newtable); } int dm_reassign_table(const char *name, char *old, char *new) diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 7a72738..7ad9f6e 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -5,6 +5,8 @@ * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include +#include +#include #include #include #include "checkers.h" @@ -14,7 +16,6 @@ #include "parser.h" #include "config.h" #include "debug.h" -#include "memory.h" #include "pgpolicies.h" #include "blacklist.h" #include "defaults.h" @@ -28,42 +29,55 @@ #include "dict.h" #include "strbuf.h" -static int -set_int(vector strvec, void *ptr) +static void +do_set_int(vector strvec, void *ptr, int min, int max, const char *file, + int line_nr, char *buff) { int *int_ptr = (int *)ptr; - char *buff, *eptr; + char *eptr; long res; - int rc; - - buff = set_value(strvec); - if (!buff) - return 1; res = strtol(buff, &eptr, 10); if (eptr > buff) while (isspace(*eptr)) eptr++; - if (*buff == '\0' || *eptr != '\0' || res > INT_MAX || res < INT_MIN) { - condlog(1, "%s: invalid value for %s: \"%s\"", - __func__, (char*)VECTOR_SLOT(strvec, 0), buff); - rc = 1; - } else { - rc = 0; - *int_ptr = res; + if (*buff == '\0' || *eptr != '\0') { + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + return; + } + if (res > max || res < min) { + res = (res > max) ? max : min; + condlog(1, "%s line %d, value for %s too %s, capping at %ld", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), + (res == max)? "large" : "small", res); } + *int_ptr = res; + return; +} + +static int +set_int(vector strvec, void *ptr, int min, int max, const char *file, + int line_nr) +{ + char *buff; - FREE(buff); - return rc; + buff = set_value(strvec); + if (!buff) + return 1; + + do_set_int(strvec, ptr, min, max, file, line_nr, buff); + + free(buff); + return 0; } static int -set_uint(vector strvec, void *ptr) +set_uint(vector strvec, void *ptr, const char *file, int line_nr) { unsigned int *uint_ptr = (unsigned int *)ptr; char *buff, *eptr, *p; unsigned long res; - int rc; buff = set_value(strvec); if (!buff) @@ -76,26 +90,23 @@ set_uint(vector strvec, void *ptr) if (eptr > buff) while (isspace(*eptr)) eptr++; - if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) { - condlog(1, "%s: invalid value for %s: \"%s\"", - __func__, (char*)VECTOR_SLOT(strvec, 0), buff); - rc = 1; - } else { - rc = 0; + if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + else *uint_ptr = res; - } - FREE(buff); - return rc; + free(buff); + return 0; } static int -set_str(vector strvec, void *ptr) +set_str(vector strvec, void *ptr, const char *file, int line_nr) { char **str_ptr = (char **)ptr; if (*str_ptr) - FREE(*str_ptr); + free(*str_ptr); *str_ptr = set_value(strvec); if (!*str_ptr) @@ -105,7 +116,73 @@ set_str(vector strvec, void *ptr) } static int -set_yes_no(vector strvec, void *ptr) +set_dir(vector strvec, void *ptr, const char *file, int line_nr) +{ + char **str_ptr = (char **)ptr; + char *old_str = *str_ptr; + struct stat sb; + + *str_ptr = set_value(strvec); + if (!*str_ptr) { + free(old_str); + return 1; + } + if ((*str_ptr)[0] != '/'){ + condlog(1, "%s line %d, %s is not an absolute directory path. Ignoring", file, line_nr, *str_ptr); + *str_ptr = old_str; + } else { + if (stat(*str_ptr, &sb) == 0 && S_ISDIR(sb.st_mode)) + free(old_str); + else { + condlog(1, "%s line %d, %s is not an existing directory. Ignoring", file, line_nr, *str_ptr); + *str_ptr = old_str; + } + } + return 0; +} + +static int +set_path(vector strvec, void *ptr, const char *file, int line_nr) +{ + char **str_ptr = (char **)ptr; + char *old_str = *str_ptr; + + *str_ptr = set_value(strvec); + if (!*str_ptr) { + free(old_str); + return 1; + } + if ((*str_ptr)[0] != '/'){ + condlog(1, "%s line %d, %s is not an absolute path. Ignoring", + file, line_nr, *str_ptr); + *str_ptr = old_str; + } else + free(old_str); + return 0; +} + +static int +set_str_noslash(vector strvec, void *ptr, const char *file, int line_nr) +{ + char **str_ptr = (char **)ptr; + char *old_str = *str_ptr; + + *str_ptr = set_value(strvec); + if (!*str_ptr) { + free(old_str); + return 1; + } + if (strchr(*str_ptr, '/')) { + condlog(1, "%s line %d, %s cannot contain a slash. Ignoring", + file, line_nr, *str_ptr); + *str_ptr = old_str; + } else + free(old_str); + return 0; +} + +static int +set_yes_no(vector strvec, void *ptr, const char *file, int line_nr) { char * buff; int *int_ptr = (int *)ptr; @@ -116,15 +193,18 @@ set_yes_no(vector strvec, void *ptr) if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YN_YES; - else + else if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) *int_ptr = YN_NO; + else + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); - FREE(buff); + free(buff); return 0; } static int -set_yes_no_undef(vector strvec, void *ptr) +set_yes_no_undef(vector strvec, void *ptr, const char *file, int line_nr) { char * buff; int *int_ptr = (int *)ptr; @@ -138,9 +218,10 @@ set_yes_no_undef(vector strvec, void *ptr) else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YNU_YES; else - *int_ptr = YNU_UNDEF; + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); - FREE(buff); + free(buff); return 0; } @@ -187,9 +268,27 @@ static int print_yes_no_undef(struct strbuf *buff, long v) #define declare_def_handler(option, function) \ static int \ -def_ ## option ## _handler (struct config *conf, vector strvec) \ +def_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ - return function (strvec, &conf->option); \ + return function (strvec, &conf->option, file, line_nr); \ +} + +#define declare_def_warn_handler(option, function) \ +static int \ +def_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ +{ \ + condlog(2, "%s line %d, \"" #option "\" is deprecated and will be disabled in a future release", file, line_nr); \ + return function (strvec, &conf->option, file, line_nr); \ +} + +#define declare_def_range_handler(option, minval, maxval) \ +static int \ +def_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ +{ \ + return set_int(strvec, &conf->option, minval, maxval, file, line_nr); \ } #define declare_def_snprint(option, function) \ @@ -224,14 +323,27 @@ snprint_def_ ## option (struct config *conf, struct strbuf *buff, \ #define declare_hw_handler(option, function) \ static int \ -hw_ ## option ## _handler (struct config *conf, vector strvec) \ +hw_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ if (!hwe) \ return 1; \ - return function (strvec, &hwe->option); \ + return function (strvec, &hwe->option, file, line_nr); \ } +#define declare_hw_range_handler(option, minval, maxval) \ +static int \ +hw_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ +{ \ + struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ + if (!hwe) \ + return 1; \ + return set_int(strvec, &hwe->option, minval, maxval, file, line_nr); \ +} + + #define declare_hw_snprint(option, function) \ static int \ snprint_hw_ ## option (struct config *conf, struct strbuf *buff, \ @@ -243,11 +355,23 @@ snprint_hw_ ## option (struct config *conf, struct strbuf *buff, \ #define declare_ovr_handler(option, function) \ static int \ -ovr_ ## option ## _handler (struct config *conf, vector strvec) \ +ovr_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ +{ \ + if (!conf->overrides) \ + return 1; \ + return function (strvec, &conf->overrides->option, file, line_nr); \ +} + +#define declare_ovr_range_handler(option, minval, maxval) \ +static int \ +ovr_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ if (!conf->overrides) \ return 1; \ - return function (strvec, &conf->overrides->option); \ + return set_int(strvec, &conf->overrides->option, minval, maxval, \ + file, line_nr); \ } #define declare_ovr_snprint(option, function) \ @@ -260,12 +384,24 @@ snprint_ovr_ ## option (struct config *conf, struct strbuf *buff, \ #define declare_mp_handler(option, function) \ static int \ -mp_ ## option ## _handler (struct config *conf, vector strvec) \ +mp_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ - return function (strvec, &mpe->option); \ + return function (strvec, &mpe->option, file, line_nr); \ +} + +#define declare_mp_range_handler(option, minval, maxval) \ +static int \ +mp_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ +{ \ + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ + if (!mpe) \ + return 1; \ + return set_int(strvec, &mpe->option, minval, maxval, file, line_nr); \ } #define declare_mp_snprint(option, function) \ @@ -277,9 +413,10 @@ snprint_mp_ ## option (struct config *conf, struct strbuf *buff, \ return function(buff, mpe->option); \ } -static int checkint_handler(struct config *conf, vector strvec) +static int checkint_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { - int rc = set_uint(strvec, &conf->checkint); + int rc = set_uint(strvec, &conf->checkint, file, line_nr); if (rc) return rc; @@ -293,24 +430,25 @@ declare_def_snprint(checkint, print_int) declare_def_handler(max_checkint, set_uint) declare_def_snprint(max_checkint, print_int) -declare_def_handler(verbosity, set_int) +declare_def_range_handler(verbosity, 0, MAX_VERBOSITY) declare_def_snprint(verbosity, print_int) declare_def_handler(reassign_maps, set_yes_no) declare_def_snprint(reassign_maps, print_yes_no) -declare_def_handler(multipath_dir, set_str) +declare_def_warn_handler(multipath_dir, set_dir) declare_def_snprint(multipath_dir, print_str) -static int def_partition_delim_handler(struct config *conf, vector strvec) +static int def_partition_delim_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { - int rc = set_str(strvec, &conf->partition_delim); + int rc = set_str_noslash(strvec, &conf->partition_delim, file, line_nr); if (rc != 0) return rc; if (!strcmp(conf->partition_delim, UNSET_PARTITION_DELIM)) { - FREE(conf->partition_delim); + free(conf->partition_delim); conf->partition_delim = NULL; } return 0; @@ -334,15 +472,12 @@ static const char * const find_multipaths_optvals[] = { }; static int -def_find_multipaths_handler(struct config *conf, vector strvec) +def_find_multipaths_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { char *buff; int i; - if (set_yes_no_undef(strvec, &conf->find_multipaths) == 0 && - conf->find_multipaths != FIND_MULTIPATHS_UNDEF) - return 0; - buff = set_value(strvec); if (!buff) return 1; @@ -355,12 +490,17 @@ def_find_multipaths_handler(struct config *conf, vector strvec) } } - if (conf->find_multipaths == YNU_UNDEF) { - condlog(0, "illegal value for find_multipaths: %s", buff); - conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; + if (i >= __FIND_MULTIPATHS_LAST) { + if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) + conf->find_multipaths = FIND_MULTIPATHS_OFF; + else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) + conf->find_multipaths = FIND_MULTIPATHS_ON; + else + condlog(1, "%s line %d, invalid value for find_multipaths: \"%s\"", + file, line_nr, buff); } - FREE(buff); + free(buff); return 0; } @@ -396,7 +536,8 @@ static int snprint_uid_attrs(struct config *conf, struct strbuf *buff, return total; } -static int uid_attrs_handler(struct config *conf, vector strvec) +static int uid_attrs_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { char *val; @@ -405,9 +546,11 @@ static int uid_attrs_handler(struct config *conf, vector strvec) if (!val) return 1; if (parse_uid_attrs(val, conf)) - condlog(1, "error parsing uid_attrs: \"%s\"", val); - condlog(3, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs)); - FREE(val); + condlog(1, "%s line %d,error parsing uid_attrs: \"%s\"", file, + line_nr, val); + else + condlog(4, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs)); + free(val); return 0; } @@ -434,11 +577,11 @@ declare_hw_snprint(prio_name, print_str) declare_mp_handler(prio_name, set_str) declare_mp_snprint(prio_name, print_str) -declare_def_handler(alias_prefix, set_str) +declare_def_handler(alias_prefix, set_str_noslash) declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX) -declare_ovr_handler(alias_prefix, set_str) +declare_ovr_handler(alias_prefix, set_str_noslash) declare_ovr_snprint(alias_prefix, print_str) -declare_hw_handler(alias_prefix, set_str) +declare_hw_handler(alias_prefix, set_str_noslash) declare_hw_snprint(alias_prefix, print_str) declare_def_handler(prio_args, set_str) @@ -466,22 +609,22 @@ declare_ovr_snprint(checker_name, print_str) declare_hw_handler(checker_name, set_str) declare_hw_snprint(checker_name, print_str) -declare_def_handler(minio, set_int) +declare_def_range_handler(minio, 0, INT_MAX) declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO) -declare_ovr_handler(minio, set_int) +declare_ovr_range_handler(minio, 0, INT_MAX) declare_ovr_snprint(minio, print_nonzero) -declare_hw_handler(minio, set_int) +declare_hw_range_handler(minio, 0, INT_MAX) declare_hw_snprint(minio, print_nonzero) -declare_mp_handler(minio, set_int) +declare_mp_range_handler(minio, 0, INT_MAX) declare_mp_snprint(minio, print_nonzero) -declare_def_handler(minio_rq, set_int) +declare_def_range_handler(minio_rq, 0, INT_MAX) declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ) -declare_ovr_handler(minio_rq, set_int) +declare_ovr_range_handler(minio_rq, 0, INT_MAX) declare_ovr_snprint(minio_rq, print_nonzero) -declare_hw_handler(minio_rq, set_int) +declare_hw_range_handler(minio_rq, 0, INT_MAX) declare_hw_snprint(minio_rq, print_nonzero) -declare_mp_handler(minio_rq, set_int) +declare_mp_range_handler(minio_rq, 0, INT_MAX) declare_mp_snprint(minio_rq, print_nonzero) declare_def_handler(queue_without_daemon, set_yes_no) @@ -505,7 +648,7 @@ snprint_def_queue_without_daemon(struct config *conf, struct strbuf *buff, return append_strbuf_quoted(buff, qwd); } -declare_def_handler(checker_timeout, set_int) +declare_def_range_handler(checker_timeout, 0, INT_MAX) declare_def_snprint(checker_timeout, print_nonzero) declare_def_handler(allow_usb_devices, set_yes_no) @@ -530,13 +673,13 @@ declare_hw_snprint(user_friendly_names, print_yes_no_undef) declare_mp_handler(user_friendly_names, set_yes_no_undef) declare_mp_snprint(user_friendly_names, print_yes_no_undef) -declare_def_handler(bindings_file, set_str) +declare_def_warn_handler(bindings_file, set_path) declare_def_snprint(bindings_file, print_str) -declare_def_handler(wwids_file, set_str) +declare_def_warn_handler(wwids_file, set_path) declare_def_snprint(wwids_file, print_str) -declare_def_handler(prkeys_file, set_str) +declare_def_warn_handler(prkeys_file, set_path) declare_def_snprint(prkeys_file, print_str) declare_def_handler(retain_hwhandler, set_yes_no_undef) @@ -576,13 +719,13 @@ declare_hw_snprint(deferred_remove, print_yes_no_undef) declare_mp_handler(deferred_remove, set_yes_no_undef) declare_mp_snprint(deferred_remove, print_yes_no_undef) -declare_def_handler(retrigger_tries, set_int) +declare_def_range_handler(retrigger_tries, 0, INT_MAX) declare_def_snprint(retrigger_tries, print_int) -declare_def_handler(retrigger_delay, set_int) +declare_def_range_handler(retrigger_delay, 0, INT_MAX) declare_def_snprint(retrigger_delay, print_int) -declare_def_handler(uev_wait_timeout, set_int) +declare_def_range_handler(uev_wait_timeout, 0, INT_MAX) declare_def_snprint(uev_wait_timeout, print_int) declare_def_handler(strict_timing, set_yes_no) @@ -597,7 +740,8 @@ declare_hw_handler(skip_kpartx, set_yes_no_undef) declare_hw_snprint(skip_kpartx, print_yes_no_undef) declare_mp_handler(skip_kpartx, set_yes_no_undef) declare_mp_snprint(skip_kpartx, print_yes_no_undef) -static int def_disable_changed_wwids_handler(struct config *conf, vector strvec) +static int def_disable_changed_wwids_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { return 0; } @@ -608,19 +752,19 @@ static int snprint_def_disable_changed_wwids(struct config *conf, return print_ignored(buff); } -declare_def_handler(remove_retries, set_int) +declare_def_range_handler(remove_retries, 0, INT_MAX) declare_def_snprint(remove_retries, print_int) -declare_def_handler(max_sectors_kb, set_int) +declare_def_range_handler(max_sectors_kb, 0, INT_MAX) declare_def_snprint(max_sectors_kb, print_nonzero) -declare_ovr_handler(max_sectors_kb, set_int) +declare_ovr_range_handler(max_sectors_kb, 0, INT_MAX) declare_ovr_snprint(max_sectors_kb, print_nonzero) -declare_hw_handler(max_sectors_kb, set_int) +declare_hw_range_handler(max_sectors_kb, 0, INT_MAX) declare_hw_snprint(max_sectors_kb, print_nonzero) -declare_mp_handler(max_sectors_kb, set_int) +declare_mp_range_handler(max_sectors_kb, 0, INT_MAX) declare_mp_snprint(max_sectors_kb, print_nonzero) -declare_def_handler(find_multipaths_timeout, set_int) +declare_def_range_handler(find_multipaths_timeout, INT_MIN, INT_MAX) declare_def_snprint_defint(find_multipaths_timeout, print_int, DEFAULT_FIND_MULTIPATHS_TIMEOUT) @@ -629,20 +773,28 @@ declare_def_snprint_defstr(enable_foreign, print_str, DEFAULT_ENABLE_FOREIGN) static int -def_config_dir_handler(struct config *conf, vector strvec) +def_config_dir_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { /* this is only valid in the main config file */ - if (conf->processed_main_config) + if (conf->processed_main_config) { + condlog(1, "%s line %d, config_dir option only valid in /etc/multipath.conf", + file, line_nr); return 0; - return set_str(strvec, &conf->config_dir); + } + condlog(2, "%s line %d, \"config_dir\" is deprecated and will be disabled in a future release", + file, line_nr); + return set_path(strvec, &conf->config_dir, file, line_nr); } declare_def_snprint(config_dir, print_str) #define declare_def_attr_handler(option, function) \ static int \ -def_ ## option ## _handler (struct config *conf, vector strvec) \ +def_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ - return function (strvec, &conf->option, &conf->attribute_flags);\ + return function (strvec, &conf->option, &conf->attribute_flags, \ + file, line_nr); \ } #define declare_def_attr_snprint(option, function) \ @@ -655,12 +807,14 @@ snprint_def_ ## option (struct config *conf, struct strbuf *buff, \ #define declare_mp_attr_handler(option, function) \ static int \ -mp_ ## option ## _handler (struct config *conf, vector strvec) \ +mp_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ - return function (strvec, &mpe->option, &mpe->attribute_flags); \ + return function (strvec, &mpe->option, &mpe->attribute_flags, \ + file, line_nr); \ } #define declare_mp_attr_snprint(option, function) \ @@ -673,7 +827,7 @@ snprint_mp_ ## option (struct config *conf, struct strbuf *buff, \ } static int -set_mode(vector strvec, void *ptr, int *flags) +set_mode(vector strvec, void *ptr, int *flags, const char *file, int line_nr) { mode_t mode; mode_t *mode_ptr = (mode_t *)ptr; @@ -687,14 +841,16 @@ set_mode(vector strvec, void *ptr, int *flags) if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { *flags |= (1 << ATTR_MODE); *mode_ptr = mode; - } + } else + condlog(1, "%s line %d, invalid value for mode: \"%s\"", + file, line_nr, buff); - FREE(buff); + free(buff); return 0; } static int -set_uid(vector strvec, void *ptr, int *flags) +set_uid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) { uid_t uid; uid_t *uid_ptr = (uid_t *)ptr; @@ -712,14 +868,16 @@ set_uid(vector strvec, void *ptr, int *flags) else if (sscanf(buff, "%u", &uid) == 1){ *flags |= (1 << ATTR_UID); *uid_ptr = uid; - } + } else + condlog(1, "%s line %d, invalid value for uid: \"%s\"", + file, line_nr, buff); - FREE(buff); + free(buff); return 0; } static int -set_gid(vector strvec, void *ptr, int *flags) +set_gid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) { gid_t gid; gid_t *gid_ptr = (gid_t *)ptr; @@ -738,8 +896,10 @@ set_gid(vector strvec, void *ptr, int *flags) else if (sscanf(buff, "%u", &gid) == 1){ *flags |= (1 << ATTR_GID); *gid_ptr = gid; - } - FREE(buff); + } else + condlog(1, "%s line %d, invalid value for gid: \"%s\"", + file, line_nr, buff); + free(buff); return 0; } @@ -786,7 +946,7 @@ declare_mp_attr_handler(gid, set_gid) declare_mp_attr_snprint(gid, print_gid) static int -set_undef_off_zero(vector strvec, void *ptr) +set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) { char * buff; int *int_ptr = (int *)ptr; @@ -797,13 +957,12 @@ set_undef_off_zero(vector strvec, void *ptr) if (strcmp(buff, "off") == 0) *int_ptr = UOZ_OFF; - else if (sscanf(buff, "%d", int_ptr) != 1 || - *int_ptr < UOZ_ZERO) - *int_ptr = UOZ_UNDEF; - else if (*int_ptr == 0) + else if (strcmp(buff, "0") == 0) *int_ptr = UOZ_ZERO; + else + do_set_int(strvec, int_ptr, 1, INT_MAX, file, line_nr, buff); - FREE(buff); + free(buff); return 0; } @@ -827,7 +986,7 @@ declare_hw_handler(fast_io_fail, set_undef_off_zero) declare_hw_snprint(fast_io_fail, print_undef_off_zero) static int -set_dev_loss(vector strvec, void *ptr) +set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr) { char * buff; unsigned int *uint_ptr = (unsigned int *)ptr; @@ -839,9 +998,10 @@ set_dev_loss(vector strvec, void *ptr) if (!strcmp(buff, "infinity")) *uint_ptr = MAX_DEV_LOSS_TMO; else if (sscanf(buff, "%u", uint_ptr) != 1) - *uint_ptr = DEV_LOSS_TMO_UNSET; + condlog(1, "%s line %d, invalid value for dev_loss_tmo: \"%s\"", + file, line_nr, buff); - FREE(buff); + free(buff); return 0; } @@ -870,17 +1030,23 @@ declare_hw_handler(eh_deadline, set_undef_off_zero) declare_hw_snprint(eh_deadline, print_undef_off_zero) static int -set_pgpolicy(vector strvec, void *ptr) +set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) { char * buff; + int policy; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; - *int_ptr = get_pgpolicy_id(buff); - FREE(buff); + policy = get_pgpolicy_id(buff); + if (policy != IOPOLICY_UNDEF) + *int_ptr = policy; + else + condlog(1, "%s line %d, invalid value for path_grouping_policy: \"%s\"", + file, line_nr, buff); + free(buff); return 0; } @@ -936,33 +1102,28 @@ get_sys_max_fds(int *max_fds) static int -max_fds_handler(struct config *conf, vector strvec) +max_fds_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { char * buff; - int r = 0, max_fds; + int max_fds; buff = set_value(strvec); if (!buff) return 1; - r = get_sys_max_fds(&max_fds); - if (r) { - /* Assume safe limit */ - max_fds = 4096; - } - if (strlen(buff) == 3 && - !strcmp(buff, "max")) + if (get_sys_max_fds(&max_fds) != 0) + max_fds = 4096; /* Assume safe limit */ + if (!strcmp(buff, "max")) conf->max_fds = max_fds; else - conf->max_fds = atoi(buff); + do_set_int(strvec, &conf->max_fds, 0, max_fds, file, line_nr, + buff); - if (conf->max_fds > max_fds) - conf->max_fds = max_fds; - - FREE(buff); + free(buff); - return r; + return 0; } static int @@ -981,7 +1142,7 @@ snprint_max_fds (struct config *conf, struct strbuf *buff, const void *data) } static int -set_rr_weight(vector strvec, void *ptr) +set_rr_weight(vector strvec, void *ptr, const char *file, int line_nr) { int *int_ptr = (int *)ptr; char * buff; @@ -993,11 +1154,12 @@ set_rr_weight(vector strvec, void *ptr) if (!strcmp(buff, "priorities")) *int_ptr = RR_WEIGHT_PRIO; - - if (!strcmp(buff, "uniform")) + else if (!strcmp(buff, "uniform")) *int_ptr = RR_WEIGHT_NONE; - - FREE(buff); + else + condlog(1, "%s line %d, invalid value for rr_weight: \"%s\"", + file, line_nr, buff); + free(buff); return 0; } @@ -1025,7 +1187,7 @@ declare_mp_handler(rr_weight, set_rr_weight) declare_mp_snprint(rr_weight, print_rr_weight) static int -set_pgfailback(vector strvec, void *ptr) +set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) { int *int_ptr = (int *)ptr; char * buff; @@ -1041,9 +1203,9 @@ set_pgfailback(vector strvec, void *ptr) else if (strlen(buff) == 10 && !strcmp(buff, "followover")) *int_ptr = -FAILBACK_FOLLOWOVER; else - *int_ptr = atoi(buff); + do_set_int(strvec, ptr, 0, INT_MAX, file, line_nr, buff); - FREE(buff); + free(buff); return 0; } @@ -1075,7 +1237,7 @@ declare_mp_handler(pgfailback, set_pgfailback) declare_mp_snprint(pgfailback, print_pgfailback) static int -no_path_retry_helper(vector strvec, void *ptr) +no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) { int *int_ptr = (int *)ptr; char * buff; @@ -1088,10 +1250,10 @@ no_path_retry_helper(vector strvec, void *ptr) *int_ptr = NO_PATH_RETRY_FAIL; else if (!strcmp(buff, "queue")) *int_ptr = NO_PATH_RETRY_QUEUE; - else if ((*int_ptr = atoi(buff)) < 1) - *int_ptr = NO_PATH_RETRY_UNDEF; + else + do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); - FREE(buff); + free(buff); return 0; } @@ -1120,7 +1282,8 @@ declare_mp_handler(no_path_retry, no_path_retry_helper) declare_mp_snprint(no_path_retry, print_no_path_retry) static int -def_log_checker_err_handler(struct config *conf, vector strvec) +def_log_checker_err_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { char * buff; @@ -1129,10 +1292,13 @@ def_log_checker_err_handler(struct config *conf, vector strvec) if (!buff) return 1; - if (strlen(buff) == 4 && !strcmp(buff, "once")) + if (!strcmp(buff, "once")) conf->log_checker_err = LOG_CHKR_ERR_ONCE; - else if (strlen(buff) == 6 && !strcmp(buff, "always")) + else if (!strcmp(buff, "always")) conf->log_checker_err = LOG_CHKR_ERR_ALWAYS; + else + condlog(1, "%s line %d, invalid value for log_checker_err: \"%s\"", + file, line_nr, buff); free(buff); return 0; @@ -1163,18 +1329,18 @@ set_reservation_key(vector strvec, struct be64 *be64_ptr, uint8_t *flags_ptr, *source_ptr = PRKEY_SOURCE_FILE; *flags_ptr = 0; put_be64(*be64_ptr, 0); - FREE(buff); + free(buff); return 0; } if (parse_prkey_flags(buff, &prkey, &sa_flags) != 0) { - FREE(buff); + free(buff); return 1; } *source_ptr = PRKEY_SOURCE_CONF; *flags_ptr = sa_flags; put_be64(*be64_ptr, prkey); - FREE(buff); + free(buff); return 0; } @@ -1193,7 +1359,8 @@ print_reservation_key(struct strbuf *buff, } static int -def_reservation_key_handler(struct config *conf, vector strvec) +def_reservation_key_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { return set_reservation_key(strvec, &conf->reservation_key, &conf->sa_flags, @@ -1209,7 +1376,8 @@ snprint_def_reservation_key (struct config *conf, struct strbuf *buff, } static int -mp_reservation_key_handler(struct config *conf, vector strvec) +mp_reservation_key_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) @@ -1229,7 +1397,7 @@ snprint_mp_reservation_key (struct config *conf, struct strbuf *buff, } static int -set_off_int_undef(vector strvec, void *ptr) +set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) { int *int_ptr = (int *)ptr; char * buff; @@ -1240,10 +1408,10 @@ set_off_int_undef(vector strvec, void *ptr) if (!strcmp(buff, "no") || !strcmp(buff, "0")) *int_ptr = NU_NO; - else if ((*int_ptr = atoi(buff)) < 1) - *int_ptr = NU_UNDEF; + else + do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); - FREE(buff); + free(buff); return 0; } @@ -1368,29 +1536,11 @@ declare_ovr_snprint(recheck_wwid, print_yes_no_undef) declare_hw_handler(recheck_wwid, set_yes_no_undef) declare_hw_snprint(recheck_wwid, print_yes_no_undef) +declare_def_range_handler(uxsock_timeout, DEFAULT_REPLY_TIMEOUT, INT_MAX) static int -def_uxsock_timeout_handler(struct config *conf, vector strvec) -{ - unsigned int uxsock_timeout; - char *buff; - - buff = set_value(strvec); - if (!buff) - return 1; - - if (sscanf(buff, "%u", &uxsock_timeout) == 1 && - uxsock_timeout > DEFAULT_REPLY_TIMEOUT) - conf->uxsock_timeout = uxsock_timeout; - else - conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; - - free(buff); - return 0; -} - -static int -hw_vpd_vendor_handler(struct config *conf, vector strvec) +hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { int i; char *buff; @@ -1408,9 +1558,10 @@ hw_vpd_vendor_handler(struct config *conf, vector strvec) goto out; } } - hwe->vpd_vendor_id = 0; + condlog(1, "%s line %d, invalid value for vpd_vendor: \"%s\"", + file, line_nr, buff); out: - FREE(buff); + free(buff); return 0; } @@ -1430,7 +1581,8 @@ snprint_hw_vpd_vendor(struct config *conf, struct strbuf *buff, * blacklist block handlers */ static int -blacklist_handler(struct config *conf, vector strvec) +blacklist_handler(struct config *conf, vector strvec, const char*file, + int line_nr) { if (!conf->blist_devnode) conf->blist_devnode = vector_alloc(); @@ -1452,7 +1604,8 @@ blacklist_handler(struct config *conf, vector strvec) } static int -blacklist_exceptions_handler(struct config *conf, vector strvec) +blacklist_exceptions_handler(struct config *conf, vector strvec, + const char *file, int line_nr) { if (!conf->elist_devnode) conf->elist_devnode = vector_alloc(); @@ -1475,7 +1628,8 @@ blacklist_exceptions_handler(struct config *conf, vector strvec) #define declare_ble_handler(option) \ static int \ -ble_ ## option ## _handler (struct config *conf, vector strvec) \ +ble_ ## option ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ char *buff; \ int rc; \ @@ -1494,7 +1648,8 @@ ble_ ## option ## _handler (struct config *conf, vector strvec) \ #define declare_ble_device_handler(name, option, vend, prod) \ static int \ -ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \ +ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec, \ + const char *file, int line_nr) \ { \ char * buff; \ int rc; \ @@ -1536,13 +1691,15 @@ snprint_ble_simple (struct config *conf, struct strbuf *buff, const void *data) } static int -ble_device_handler(struct config *conf, vector strvec) +ble_device_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { return alloc_ble_device(conf->blist_device); } static int -ble_except_device_handler(struct config *conf, vector strvec) +ble_except_device_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { return alloc_ble_device(conf->elist_device); } @@ -1574,7 +1731,8 @@ static int snprint_bled_product(struct config *conf, struct strbuf *buff, * devices block handlers */ static int -devices_handler(struct config *conf, vector strvec) +devices_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { if (!conf->hwtable) conf->hwtable = vector_alloc(); @@ -1586,7 +1744,8 @@ devices_handler(struct config *conf, vector strvec) } static int -device_handler(struct config *conf, vector strvec) +device_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { struct hwentry * hwe; @@ -1623,7 +1782,8 @@ declare_hw_snprint(hwhandler, print_str) * overrides handlers */ static int -overrides_handler(struct config *conf, vector strvec) +overrides_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { if (!conf->overrides) conf->overrides = alloc_hwe(); @@ -1640,7 +1800,8 @@ overrides_handler(struct config *conf, vector strvec) * multipaths block handlers */ static int -multipaths_handler(struct config *conf, vector strvec) +multipaths_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { if (!conf->mptable) conf->mptable = vector_alloc(); @@ -1652,7 +1813,8 @@ multipaths_handler(struct config *conf, vector strvec) } static int -multipath_handler(struct config *conf, vector strvec) +multipath_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { struct mpentry * mpe; @@ -1673,7 +1835,7 @@ multipath_handler(struct config *conf, vector strvec) declare_mp_handler(wwid, set_str) declare_mp_snprint(wwid, print_str) -declare_mp_handler(alias, set_str) +declare_mp_handler(alias, set_str_noslash) declare_mp_snprint(alias, print_str) /* @@ -1681,7 +1843,8 @@ declare_mp_snprint(alias, print_str) */ static int -deprecated_handler(struct config *conf, vector strvec) +deprecated_handler(struct config *conf, vector strvec, const char *file, + int line_nr) { char * buff; @@ -1690,7 +1853,7 @@ deprecated_handler(struct config *conf, vector strvec) if (!buff) return 1; - FREE(buff); + free(buff); return 0; } diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index f25fe9e..7d939ae 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -17,7 +17,6 @@ #include "checkers.h" #include "vector.h" -#include "memory.h" #include "util.h" #include "structs.h" #include "config.h" @@ -37,6 +36,8 @@ #include "print.h" #include "strbuf.h" +#define VPD_BUFLEN 4096 + struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = { [VPD_VP_UNDEF] = { 0x00, "undef" }, [VPD_VP_HP3PAR] = { 0xc0, "hp3par" }, @@ -388,8 +389,10 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) if (value && !strcmp(value, "usb")) { pp->sg_id.proto_id = SCSI_PROTOCOL_USB; tgtname = udev_device_get_sysname(tgtdev); - strlcpy(node, tgtname, NODE_NAME_SIZE); - return 0; + if (tgtname) { + strlcpy(node, tgtname, NODE_NAME_SIZE); + return 0; + } } tgtdev = udev_device_get_parent(tgtdev); } @@ -803,7 +806,7 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) parent = udev_device_get_parent(parent)) { const char *ed = udev_device_get_sysname(parent); - if (!strncmp(ed, ed_str, sizeof(ed_str) - 1)) { + if (ed && !strncmp(ed, ed_str, sizeof(ed_str) - 1)) { end_dev_id = ed; break; } @@ -1085,6 +1088,8 @@ parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len) if (out_len == 0) return 0; + if (len > WWID_SIZE) + len = WWID_SIZE; /* * Strip leading and trailing whitespace */ @@ -1114,177 +1119,197 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, const unsigned char *d; const unsigned char *vpd = NULL; size_t len, vpd_len, i; - int vpd_type, prio = -1, naa_prio; + int vpd_type, prio = -1; + int err = -ENODATA; + STRBUF_ON_STACK(buf); + + /* Need space at least for one digit */ + if (out_len <= 1) + return 0; d = in + 4; - while (d < in + in_len) { + while (d <= in + in_len - 4) { + bool invalid = false; + int new_prio = -1; + /* Select 'association: LUN' */ - if ((d[1] & 0x30) != 0) { - d += d[3] + 4; - continue; - } + if ((d[1] & 0x30) == 0x30) { + invalid = true; + goto next_designator; + } else if ((d[1] & 0x30) != 0x00) + goto next_designator; + switch (d[1] & 0xf) { + unsigned char good_len; case 0x3: /* NAA: Prio 5 */ switch (d[4] >> 4) { case 6: /* IEEE Registered Extended: Prio 8 */ - naa_prio = 8; + new_prio = 8; + good_len = 16; break; case 5: /* IEEE Registered: Prio 7 */ - naa_prio = 7; + new_prio = 7; + good_len = 8; break; case 2: /* IEEE Extended: Prio 6 */ - naa_prio = 6; + new_prio = 6; + good_len = 8; break; case 3: /* IEEE Locally assigned: Prio 1 */ - naa_prio = 1; + new_prio = 1; + good_len = 8; break; default: /* Default: no priority */ - naa_prio = -1; + good_len = 0xff; break; } - if (prio < naa_prio) { - prio = naa_prio; - vpd = d; - } + + invalid = good_len == 0xff || good_len != d[3]; break; case 0x2: /* EUI-64: Prio 4 */ - if (prio < 4) { - prio = 4; - vpd = d; - } + invalid = (d[3] != 8 && d[3] != 12 && d[3] != 16); + new_prio = 4; break; case 0x8: /* SCSI Name: Prio 3 */ - if (memcmp(d + 4, "eui.", 4) && - memcmp(d + 4, "naa.", 4) && - memcmp(d + 4, "iqn.", 4)) - break; - if (prio < 3) { - prio = 3; - vpd = d; - } + invalid = (d[3] < 4 || (memcmp(d + 4, "eui.", 4) && + memcmp(d + 4, "naa.", 4) && + memcmp(d + 4, "iqn.", 4))); + new_prio = 3; break; case 0x1: /* T-10 Vendor ID: Prio 2 */ - if (prio < 2) { - prio = 2; - vpd = d; - } + invalid = (d[3] < 8); + new_prio = 2; + break; + case 0xa: + condlog(2, "%s: UUID identifiers not yet supported", + __func__); + break; + default: + invalid = true; break; } + + next_designator: + if (d + d[3] + 4 - in > (ssize_t)in_len) { + condlog(2, "%s: device descriptor length overflow: %zd > %zu", + __func__, d + d[3] + 4 - in, in_len); + err = -EOVERFLOW; + break; + } else if (invalid) { + condlog(2, "%s: invalid device designator at offset %zd: %02x%02x%02x%02x", + __func__, d - in, d[0], d[1], d[2], d[3]); + /* + * We checked above that the next offset is within limits. + * Proceed, fingers crossed. + */ + err = -EINVAL; + } else if (new_prio > prio) { + vpd = d; + prio = new_prio; + } d += d[3] + 4; } if (prio <= 0) - return -ENODATA; - /* Need space at least for one digit */ - else if (out_len <= 1) - return 0; + return err; + + if (d != in + in_len) + /* Should this be fatal? (overflow covered above) */ + condlog(2, "%s: warning: last descriptor end %zd != VPD length %zu", + __func__, d - in, in_len); len = 0; vpd_type = vpd[1] & 0xf; vpd_len = vpd[3]; vpd += 4; + /* untaint vpd_len for coverity */ + if (vpd_len > WWID_SIZE) { + condlog(1, "%s: suspicious designator length %zu truncated to %u", + __func__, vpd_len, WWID_SIZE); + vpd_len = WWID_SIZE; + } if (vpd_type == 0x2 || vpd_type == 0x3) { size_t i; - len = sprintf(out, "%d", vpd_type); - if (2 * vpd_len >= out_len - len) { - condlog(1, "%s: WWID overflow, type %d, %zu/%zu bytes required", - __func__, vpd_type, - 2 * vpd_len + len + 1, out_len); - vpd_len = (out_len - len - 1) / 2; - } + if ((err = print_strbuf(&buf, "%d", vpd_type)) < 0) + return err; for (i = 0; i < vpd_len; i++) - len += sprintf(out + len, - "%02x", vpd[i]); - } else if (vpd_type == 0x8 && vpd_len < 4) { - condlog(1, "%s: VPD length %zu too small for designator type 8", - __func__, vpd_len); - return -EINVAL; + if ((err = print_strbuf(&buf, "%02x", vpd[i])) < 0) + return err; } else if (vpd_type == 0x8) { + char type; + if (!memcmp("eui.", vpd, 4)) - out[0] = '2'; + type = '2'; else if (!memcmp("naa.", vpd, 4)) - out[0] = '3'; + type = '3'; else - out[0] = '8'; + type = '8'; + if ((err = fill_strbuf(&buf, type, 1)) < 0) + return err; vpd += 4; len = vpd_len - 4; - while (len > 2 && vpd[len - 2] == '\0') - --len; - if (len > out_len - 1) { - condlog(1, "%s: WWID overflow, type 8/%c, %zu/%zu bytes required", - __func__, out[0], len + 1, out_len); - len = out_len - 1; - } + if ((err = __append_strbuf_str(&buf, (const char *)vpd, len)) < 0) + return err; - if (out[0] == '8') - for (i = 0; i < len; ++i) - out[1 + i] = vpd[i]; - else - for (i = 0; i < len; ++i) - out[1 + i] = tolower(vpd[i]); + /* The input is 0-padded, make sure the length is correct */ + truncate_strbuf(&buf, strlen(get_strbuf_str(&buf))); + len = get_strbuf_len(&buf); + if (type != '8') { + char *buffer = __get_strbuf_buf(&buf); - /* designator should be 0-terminated, but let's make sure */ - out[len] = '\0'; + for (i = 0; i < len; ++i) + buffer[i] = tolower(buffer[i]); + } } else if (vpd_type == 0x1) { const unsigned char *p; size_t p_len; - out[0] = '1'; - len = 1; - while ((p = memchr(vpd, ' ', vpd_len))) { + if ((err = fill_strbuf(&buf, '1', 1)) < 0) + return err; + while (vpd && (p = memchr(vpd, ' ', vpd_len))) { p_len = p - vpd; - if (len + p_len > out_len - 1) { - condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required", - __func__, len + p_len, out_len); - p_len = out_len - len - 1; - } - memcpy(out + len, vpd, p_len); - len += p_len; - if (len >= out_len - 1) { - out[len] = '\0'; - break; - } - out[len] = '_'; - len ++; - if (len >= out_len - 1) { - out[len] = '\0'; - break; - } + if ((err = __append_strbuf_str(&buf, (const char *)vpd, + p_len)) < 0) + return err; vpd = p; vpd_len -= p_len; - while (vpd && *vpd == ' ') { + while (vpd && vpd_len > 0 && *vpd == ' ') { vpd++; vpd_len --; } + if (vpd_len > 0 && (err = fill_strbuf(&buf, '_', 1)) < 0) + return err; } - p_len = vpd_len; - if (p_len > 0 && len < out_len - 1) { - if (len + p_len > out_len - 1) { - condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required", - __func__, len + p_len + 1, out_len); - p_len = out_len - len - 1; - } - memcpy(out + len, vpd, p_len); - len += p_len; - out[len] = '\0'; - } - if (len > 1 && out[len - 1] == '_') { - out[len - 1] = '\0'; - len--; + if (vpd_len > 0) { + if ((err = __append_strbuf_str(&buf, (const char *)vpd, + vpd_len)) < 0) + return err; } } + + len = get_strbuf_len(&buf); + if (len >= out_len) { + condlog(1, "%s: WWID overflow, type %d, %zu/%zu bytes required", + __func__, vpd_type, len, out_len); + if (vpd_type == 2 || vpd_type == 3) + /* designator must have an even number of characters */ + len = 2 * (out_len / 2) - 1; + else + len = out_len - 1; + } + strlcpy(out, get_strbuf_str(&buf), len + 1); return len; } @@ -1315,11 +1340,12 @@ parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len, static int get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) { - int len, buff_len; - unsigned char buff[4096]; + int len; + size_t buff_len; + unsigned char buff[VPD_BUFLEN]; - memset(buff, 0x0, 4096); - if (!parent || sysfs_get_vpd(parent, pg, buff, 4096) <= 0) { + 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; } @@ -1330,8 +1356,10 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) return -ENODATA; } buff_len = get_unaligned_be16(&buff[2]) + 4; - if (buff_len > 4096) + if (buff_len > VPD_BUFLEN) { condlog(3, "vpd pg%02x page truncated", pg); + buff_len = VPD_BUFLEN; + } if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); @@ -1375,7 +1403,7 @@ bool is_vpd_page_supported(int fd, int pg) { int i, len; - unsigned char buff[4096]; + unsigned char buff[VPD_BUFLEN]; len = fetch_vpd_page(fd, 0x00, buff, sizeof(buff)); if (len < 0) @@ -1391,7 +1419,7 @@ int get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen) { int len, buff_len; - unsigned char buff[4096]; + unsigned char buff[VPD_BUFLEN]; buff_len = fetch_vpd_page(fd, pg, buff, sizeof(buff)); if (buff_len < 0) @@ -2363,15 +2391,16 @@ int pathinfo(struct path *pp, struct config *conf, int mask) } if ((mask & DI_WWID) && !strlen(pp->wwid)) { - get_uid(pp, path_state, pp->udev, - (pp->retriggers >= conf->retrigger_tries)); + int allow_fallback = ((mask & DI_NOFALLBACK) == 0 && + pp->retriggers >= conf->retrigger_tries); + get_uid(pp, path_state, pp->udev, allow_fallback); if (!strlen(pp->wwid)) { if (pp->bus == SYSFS_BUS_UNDEF) return PATHINFO_SKIPPED; if (pp->initialized != INIT_FAILED) { pp->initialized = INIT_MISSING_UDEV; pp->tick = conf->retrigger_delay; - } else if (pp->retriggers >= conf->retrigger_tries && + } else if (allow_fallback && (pp->state == PATH_UP || pp->state == PATH_GHOST)) { /* * We have failed to read udev info for this path diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index a5446b4..095657b 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -70,6 +70,7 @@ enum discovery_mode { __DI_WWID, __DI_BLACKLIST, __DI_NOIO, + __DI_NOFALLBACK, }; #define DI_SYSFS (1 << __DI_SYSFS) @@ -79,6 +80,7 @@ enum discovery_mode { #define DI_WWID (1 << __DI_WWID) #define DI_BLACKLIST (1 << __DI_BLACKLIST) #define DI_NOIO (1 << __DI_NOIO) /* Avoid IO on the device */ +#define DI_NOFALLBACK (1 << __DI_NOFALLBACK) /* do not allow wwid fallback */ #define DI_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \ DI_WWID) diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index 4ba7f33..74c9215 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -10,7 +10,6 @@ #include "checkers.h" #include "vector.h" -#include "memory.h" #include "structs.h" #include "util.h" #include "debug.h" @@ -27,7 +26,7 @@ merge_words(char **dst, const char *word) dstlen = strlen(*dst); len = dstlen + strlen(word) + 2; - *dst = REALLOC(*dst, len); + *dst = realloc(*dst, len); if (!*dst) { free(p); @@ -146,11 +145,11 @@ int disassemble_map(const struct _vector *pathvec, return 1; if (merge_words(&mpp->features, word)) { - FREE(word); + free(word); return 1; } - FREE(word); + free(word); } /* @@ -170,10 +169,10 @@ int disassemble_map(const struct _vector *pathvec, return 1; if (merge_words(&mpp->hwhandler, word)) { - FREE(word); + free(word); return 1; } - FREE(word); + free(word); } /* @@ -185,7 +184,7 @@ int disassemble_map(const struct _vector *pathvec, return 1; num_pg = atoi(word); - FREE(word); + free(word); if (num_pg > 0) { if (!mpp->pg) { @@ -207,7 +206,7 @@ int disassemble_map(const struct _vector *pathvec, goto out; mpp->nextpg = atoi(word); - FREE(word); + free(word); for (i = 0; i < num_pg; i++) { /* @@ -232,7 +231,7 @@ int disassemble_map(const struct _vector *pathvec, if (merge_words(&mpp->selector, word)) goto out1; - FREE(word); + free(word); } else { p += get_word(p, NULL); p += get_word(p, NULL); @@ -260,7 +259,7 @@ int disassemble_map(const struct _vector *pathvec, goto out; num_paths = atoi(word); - FREE(word); + free(word); p += get_word(p, &word); @@ -268,7 +267,7 @@ int disassemble_map(const struct _vector *pathvec, goto out; num_paths_args = atoi(word); - FREE(word); + free(word); for (j = 0; j < num_paths; j++) { pp = NULL; @@ -294,7 +293,7 @@ int disassemble_map(const struct _vector *pathvec, } else if (store_path(pgp->paths, pp)) goto out1; - FREE(word); + free(word); pgp->id ^= (long)pp; pp->pgindex = i + 1; @@ -303,7 +302,7 @@ int disassemble_map(const struct _vector *pathvec, if (k == 0) { p += get_word(p, &word); def_minio = atoi(word); - FREE(word); + free(word); if (!strncmp(mpp->selector, "round-robin", 11)) { @@ -324,7 +323,7 @@ int disassemble_map(const struct _vector *pathvec, } return 0; out1: - FREE(word); + free(word); out: free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; @@ -358,7 +357,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; num_feature_args = atoi(word); - FREE(word); + free(word); for (i = 0; i < num_feature_args; i++) { if (i == 1) { @@ -368,7 +367,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; mpp->queuedio = atoi(word); - FREE(word); + free(word); continue; } /* unknown */ @@ -383,7 +382,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; num_hwhandler_args = atoi(word); - FREE(word); + free(word); for (i = 0; i < num_hwhandler_args; i++) p += get_word(p, NULL); @@ -397,7 +396,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; num_pg = atoi(word); - FREE(word); + free(word); if (num_pg == 0) return 0; @@ -434,7 +433,7 @@ int disassemble_status(const char *params, struct multipath *mpp) pgp->status = PGSTATE_UNDEF; break; } - FREE(word); + free(word); /* * PG Status (discarded, would be '0' anyway) @@ -447,7 +446,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; num_paths = atoi(word); - FREE(word); + free(word); p += get_word(p, &word); @@ -455,7 +454,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; num_pg_args = atoi(word); - FREE(word); + free(word); if (VECTOR_SIZE(pgp->paths) < num_paths) return 1; @@ -485,7 +484,7 @@ int disassemble_status(const char *params, struct multipath *mpp) default: break; } - FREE(word); + free(word); /* * fail count */ @@ -495,7 +494,7 @@ int disassemble_status(const char *params, struct multipath *mpp) return 1; pp->failcount = atoi(word); - FREE(word); + free(word); /* * selector args @@ -508,7 +507,7 @@ int disassemble_status(const char *params, struct multipath *mpp) &def_minio) == 1 && def_minio != mpp->minio) mpp->minio = def_minio; - FREE(word); + free(word); } else p += get_word(p, NULL); } diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index e091a1d..e80eb3e 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -30,10 +30,10 @@ #include "vector.h" #include "debug.h" #include "util.h" -#include "foreign.h" #include "structs.h" #include "structs_vec.h" #include "print.h" +#include "foreign.h" #include "strbuf.h" static vector foreigns; @@ -437,7 +437,7 @@ void check_foreign(void) } /* Call this after get_path_layout */ -void foreign_path_layout(void) +void foreign_path_layout(fieldwidth_t *width) { struct foreign *fgn; int i; @@ -457,7 +457,7 @@ void foreign_path_layout(void) vec = fgn->get_paths(fgn->context); if (vec != NULL) { - _get_path_layout(vec, LAYOUT_RESET_NOT); + _get_path_layout(vec, LAYOUT_RESET_NOT, width); } fgn->release_paths(fgn->context, vec); @@ -468,7 +468,7 @@ void foreign_path_layout(void) } /* Call this after get_multipath_layout */ -void foreign_multipath_layout(void) +void foreign_multipath_layout(fieldwidth_t *width) { struct foreign *fgn; int i; @@ -488,7 +488,7 @@ void foreign_multipath_layout(void) vec = fgn->get_multipaths(fgn->context); if (vec != NULL) { - _get_multipath_layout(vec, LAYOUT_RESET_NOT); + _get_multipath_layout(vec, LAYOUT_RESET_NOT, width); } fgn->release_multipaths(fgn->context, vec); @@ -498,19 +498,13 @@ void foreign_multipath_layout(void) pthread_cleanup_pop(1); } -int snprint_foreign_topology(struct strbuf *buf, int verbosity) +static int __snprint_foreign_topology(struct strbuf *buf, int verbosity, + const fieldwidth_t *width) { struct foreign *fgn; int i; size_t initial_len = get_strbuf_len(buf); - rdlock_foreigns(); - if (foreigns == NULL) { - unlock_foreigns(NULL); - return 0; - } - pthread_cleanup_push(unlock_foreigns, NULL); - vector_foreach_slot(foreigns, fgn, i) { const struct _vector *vec; const struct gen_multipath *gm; @@ -523,7 +517,7 @@ int snprint_foreign_topology(struct strbuf *buf, int verbosity) if (vec != NULL) { vector_foreach_slot(vec, gm, j) { if (_snprint_multipath_topology( - gm, buf, verbosity) < 0) + gm, buf, verbosity, width) < 0) break; } } @@ -531,19 +525,57 @@ int snprint_foreign_topology(struct strbuf *buf, int verbosity) pthread_cleanup_pop(1); } - pthread_cleanup_pop(1); return get_strbuf_len(buf) - initial_len; } +int snprint_foreign_topology(struct strbuf *buf, int verbosity, + const fieldwidth_t *width) +{ + int rc; + + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return 0; + } + pthread_cleanup_push(unlock_foreigns, NULL); + rc = __snprint_foreign_topology(buf, verbosity, width); + pthread_cleanup_pop(1); + return rc; +} + void print_foreign_topology(int verbosity) { STRBUF_ON_STACK(buf); + struct foreign *fgn; + int i; + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; + + if ((width = alloc_path_layout()) == NULL) + return; + rdlock_foreigns(); + if (foreigns == NULL) { + unlock_foreigns(NULL); + return; + } + pthread_cleanup_push(unlock_foreigns, NULL); + vector_foreach_slot(foreigns, fgn, i) { + const struct _vector *vec; - snprint_foreign_topology(&buf, verbosity); + fgn->lock(fgn->context); + pthread_cleanup_push(fgn->unlock, fgn->context); + vec = fgn->get_paths(fgn->context); + _get_multipath_layout(vec, LAYOUT_RESET_NOT, width); + fgn->release_paths(fgn->context, vec); + pthread_cleanup_pop(1); + } + __snprint_foreign_topology(&buf, verbosity, width); + pthread_cleanup_pop(1); printf("%s", get_strbuf_str(&buf)); } -int snprint_foreign_paths(struct strbuf *buf, const char *style, int pretty) +int snprint_foreign_paths(struct strbuf *buf, const char *style, + const fieldwidth_t *width) { struct foreign *fgn; int i; @@ -567,7 +599,7 @@ int snprint_foreign_paths(struct strbuf *buf, const char *style, int pretty) vec = fgn->get_paths(fgn->context); if (vec != NULL) { vector_foreach_slot(vec, gp, j) { - ret = _snprint_path(gp, buf, style, pretty); + ret = _snprint_path(gp, buf, style, width); if (ret < 0) break; } @@ -582,8 +614,8 @@ int snprint_foreign_paths(struct strbuf *buf, const char *style, int pretty) return get_strbuf_len(buf) - initial_len; } -int snprint_foreign_multipaths(struct strbuf *buf, - const char *style, int pretty) +int snprint_foreign_multipaths(struct strbuf *buf, const char *style, + const fieldwidth_t *width) { struct foreign *fgn; int i; @@ -608,7 +640,7 @@ int snprint_foreign_multipaths(struct strbuf *buf, if (vec != NULL) { vector_foreach_slot(vec, gm, j) { ret = _snprint_multipath(gm, buf, - style, pretty); + style, width); if (ret < 0) break; } diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h index 77fc485..cf8f570 100644 --- a/libmultipath/foreign.h +++ b/libmultipath/foreign.h @@ -18,7 +18,7 @@ #define _FOREIGN_H #include #include -#define LIBMP_FOREIGN_API ((1 << 8) | 1) +#define LIBMP_FOREIGN_API ((1 << 8) | 2) struct strbuf; struct context; @@ -252,15 +252,17 @@ void check_foreign(void); * foreign_path_layout() * call this before printing paths, after get_path_layout(), to determine * output field width. + * @param width: an array allocated by alloc_path_layout() */ -void foreign_path_layout(void); +void foreign_path_layout(fieldwidth_t *width); /** * foreign_multipath_layout() * call this before printing maps, after get_multipath_layout(), to determine * output field width. + * @param width: an array allocated by alloc_multipath_layout() */ -void foreign_multipath_layout(void); +void foreign_multipath_layout(fieldwidth_t *width); /** * snprint_foreign_topology(buf, len, verbosity); @@ -268,9 +270,11 @@ void foreign_multipath_layout(void); * '\0' - terminated. * @param buf: output buffer * @param verbosity: verbosity level + * @param width: an array of field widths, initialized by _get_path_layout() * @returns: number of printed characters excluding trailing '\0'. */ -int snprint_foreign_topology(struct strbuf *buf, int verbosity); +int snprint_foreign_topology(struct strbuf *buf, int verbosity, + const fieldwidth_t *width); /** * snprint_foreign_paths(buf, len, style, pad); @@ -278,10 +282,11 @@ int snprint_foreign_topology(struct strbuf *buf, int verbosity); * '\0' - terminated. * @param buf: output buffer * @param style: format string - * @param pad: whether to pad field width + * @param width: array initialized with get_path_layout(), or NULL for no padding * @returns: number of printed characters excluding trailing '\0'. */ -int snprint_foreign_paths(struct strbuf *buf, const char *style, int pad); +int snprint_foreign_paths(struct strbuf *buf, const char *style, + const fieldwidth_t *width); /** * snprint_foreign_multipaths(buf, len, style, pad); @@ -289,11 +294,11 @@ int snprint_foreign_paths(struct strbuf *buf, const char *style, int pad); * '\0' - terminated. * @param buf: output buffer * @param style: format string - * @param pad: whether to pad field width + * @param width: array initialized with get_path_layout(), or NULL for no padding * @returns: number of printed characters excluding trailing '\0'. */ -int snprint_foreign_multipaths(struct strbuf *buf, - const char *style, int pretty); +int snprint_foreign_multipaths(struct strbuf *buf, const char *style, + const fieldwidth_t *width); /** * print_foreign_topology(v) diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c index d40c086..499b881 100644 --- a/libmultipath/foreign/nvme.c +++ b/libmultipath/foreign/nvme.c @@ -184,7 +184,9 @@ static int snprint_nvme_map(const struct gen_multipath *gmp, "firmware_rev")); case 'r': val = udev_device_get_sysattr_value(nvm->udev, "ro"); - if (val[0] == 1) + if (!val) + return append_strbuf_str(buff, "undef"); + else if (val[0] == 1) return append_strbuf_str(buff, "ro"); else return append_strbuf_str(buff, "rw"); diff --git a/libmultipath/generic.h b/libmultipath/generic.h index 57c123c..e89848f 100644 --- a/libmultipath/generic.h +++ b/libmultipath/generic.h @@ -18,6 +18,14 @@ #define _GENERIC_H #include "vector.h" +/* + * fieldwidth_t is required in print.h and foreign.h. + * Defining it twice is not allowed before C11. + * So do it here. + */ +typedef unsigned char fieldwidth_t; +#define MAX_FIELD_WIDTH UCHAR_MAX + struct strbuf; struct gen_multipath; struct gen_pathgroup; diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 0caac0d..c65e5e1 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -359,14 +359,11 @@ static struct hwentry default_hw[] = { .pgpolicy = MULTIBUS, }, { - /* - * SC Series, formerly Compellent - * - * Maintainer: Sean McGinnis - */ + /* SC Series, formerly Compellent */ .vendor = "COMPELNT", .product = "Compellent Vol", - .pgpolicy = MULTIBUS, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = NO_PATH_RETRY_QUEUE, }, { @@ -399,6 +396,15 @@ static struct hwentry default_hw[] = { .no_path_retry = 3, .fast_io_fail = 15, }, + { + /* PowerVault ME4 */ + .vendor = "DellEMC", + .product = "ME4", + .pgpolicy = GROUP_BY_PRIO, + .prio_name = PRIO_ALUA, + .hwhandler = "1 alua", + .pgfailback = -FAILBACK_IMMEDIATE, + }, /* * Fujitsu */ @@ -483,8 +489,6 @@ static struct hwentry default_hw[] = { }, /* * IBM - * - * Maintainer: Hannes Reinecke */ { /* ProFibre 4000R */ @@ -656,7 +660,8 @@ static struct hwentry default_hw[] = { .vendor = "IBM", .product = "^2107900", .no_path_retry = NO_PATH_RETRY_QUEUE, - .pgpolicy = MULTIBUS, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, }, { // Storwize V5000 and V7000 lines / SAN Volume Controller (SVC) / Flex System V7000 / @@ -712,7 +717,8 @@ static struct hwentry default_hw[] = { .vendor = "(XIV|IBM)", .product = "(NEXTRA|2810XIV)", .no_path_retry = NO_PATH_RETRY_QUEUE, - .pgpolicy = MULTIBUS, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = 15, }, { /* TMS RamSan / FlashSystem 710/720/810/820/840/900 */ diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c index d8d91f6..dc1c252 100644 --- a/libmultipath/io_err_stat.c +++ b/libmultipath/io_err_stat.c @@ -24,7 +24,6 @@ #include #include "vector.h" -#include "memory.h" #include "checkers.h" #include "config.h" #include "structs.h" @@ -132,7 +131,7 @@ static int setup_directio_ctx(struct io_err_stat_path *p) if (p->fd < 0) return 1; - p->dio_ctx_array = MALLOC(sizeof(struct dio_ctx) * CONCUR_NR_EVENT); + p->dio_ctx_array = calloc(1, sizeof(struct dio_ctx) * CONCUR_NR_EVENT); if (!p->dio_ctx_array) goto fail_close; @@ -154,7 +153,8 @@ deinit: for (i = 0; i < CONCUR_NR_EVENT; i++) deinit_each_dio_ctx(p->dio_ctx_array + i); free_pdctx: - FREE(p->dio_ctx_array); + free(p->dio_ctx_array); + p->dio_ctx_array = NULL; fail_close: close(p->fd); @@ -174,19 +174,19 @@ static void free_io_err_stat_path(struct io_err_stat_path *p) for (i = 0; i < CONCUR_NR_EVENT; i++) deinit_each_dio_ctx(p->dio_ctx_array + i); - FREE(p->dio_ctx_array); + free(p->dio_ctx_array); if (p->fd > 0) close(p->fd); free_path: - FREE(p); + free(p); } static struct io_err_stat_path *alloc_io_err_stat_path(void) { struct io_err_stat_path *p; - p = (struct io_err_stat_path *)MALLOC(sizeof(*p)); + p = (struct io_err_stat_path *)calloc(1, sizeof(*p)); if (!p) return NULL; diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version index eb5b5b5..9c7ffa7 100644 --- a/libmultipath/libmultipath.version +++ b/libmultipath/libmultipath.version @@ -31,27 +31,32 @@ * The new version inherits the previous ones. */ -LIBMULTIPATH_9.0.0 { +LIBMULTIPATH_13.0.0 { global: /* symbols referenced by multipath and multipathd */ add_foreign; add_map_with_path; adopt_paths; alloc_multipath; + alloc_multipath_layout; alloc_path; + alloc_path_layout; alloc_path_with_pathinfo; alloc_strvec; + append_strbuf_str; change_foreign; check_alias_settings; + check_daemon; checker_clear_message; checker_disable; checker_enable; - checker_is_sync; checker_message; checker_name; checker_state_name; check_foreign; + cleanup_charp; cleanup_lock; + cleanup_ucharp; close_fd; coalesce_paths; convert_dev; @@ -74,6 +79,7 @@ global: dm_is_mpath; dm_mapname; dm_map_present; + dm_prereq; dm_queue_if_no_path; dm_reassign; dm_reinstate_path; @@ -102,16 +108,21 @@ global: 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; + get_vpd_sgio; group_by_prio; init_checkers; + init_config; init_foreign; init_prio; io_err_stat_handle_pathfail; @@ -119,15 +130,21 @@ global: is_quote; libmp_dm_task_create; libmp_get_version; + libmp_get_multipath_config; + libmp_dm_task_run; + libmp_put_multipath_config; libmp_udev_set_sync_support; + libmp_verbosity; + libmultipath_exit; + libmultipath_init; load_config; log_thread_reset; log_thread_start; log_thread_stop; + logsink; need_io_err_check; normalize_timespec; orphan_path; - orphan_paths; parse_prkey_flags; pathcount; path_discovery; @@ -137,9 +154,10 @@ global: print_all_paths; print_foreign_topology; _print_multipath_topology; + print_strbuf; pthread_cond_init_mono; + put_multipath_config; recv_packet; - recv_packet_from_client; reinstate_paths; remember_wwid; remove_map; @@ -148,6 +166,7 @@ global: remove_wwid; replace_wwids; reset_checker_classes; + reset_strbuf; select_all_tg_pt; select_action; select_find_multipaths_timeout; @@ -162,7 +181,9 @@ global: setup_map; setup_thread_attr; should_multipath; + skip_libmp_dm_init; snprint_blacklist_report; + __snprint_config; snprint_config; snprint_devices; snprint_foreign_multipaths; @@ -186,17 +207,20 @@ global: sysfs_attr_set_value; sysfs_get_size; sysfs_is_multipathed; + timespeccmp; timespecsub; trigger_paths_udev_change; + truncate_strbuf; + udev; uevent_dispatch; uevent_get_dm_str; uevent_get_env_positive_int; uevent_is_mpath; uevent_listen; + uninit_config; update_mpp_paths; update_multipath_strings; update_multipath_table; - update_pathvec_from_dm; update_queue_mode_add_path; update_queue_mode_del_path; ux_socket_listen; @@ -210,10 +234,14 @@ global: verify_paths; /* checkers */ + checker_is_sync; sg_read; + 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; @@ -222,6 +250,7 @@ global: libmp_nvme_identify_ns; log_nvme_errcode; nvme_id_ctrl_ana; + set_wakeup_fn; snprint_host_wwnn; snprint_host_wwpn; snprint_path_serial; @@ -233,57 +262,6 @@ global: free_scandir_result; sysfs_attr_get_value; - /* added in 2.1.0 */ - libmp_dm_task_run; - cleanup_mutex; - - /* added in 2.2.0 */ - libmp_get_multipath_config; - get_multipath_config; - libmp_put_multipath_config; - put_multipath_config; - init_config; - uninit_config; - - /* added in 2.3.0 */ - udev; - logsink; - libmultipath_init; - libmultipath_exit; - - /* added in 4.1.0 */ - libmp_verbosity; - - /* added in 4.2.0 */ - dm_prereq; - skip_libmp_dm_init; - - /* added in 4.3.0 */ - start_checker_thread; - - /* added in 4.4.0 */ - get_next_string; - - /* added in 4.5.0 */ - get_vpd_sgio; - trigger_partitions_udev_change; - - /* added in 7.0.0 */ - cleanup_charp; - - /* added in 8.1.0 */ - reset_strbuf; - append_strbuf_str; - get_strbuf_len; - get_strbuf_str; - steal_strbuf_str; - fill_strbuf; - print_strbuf; - truncate_strbuf; - - /* added in 8.2.0 */ - check_daemon; - local: *; }; diff --git a/libmultipath/lock.c b/libmultipath/lock.c index 72c70e3..93b48db 100644 --- a/libmultipath/lock.c +++ b/libmultipath/lock.c @@ -3,6 +3,16 @@ void cleanup_lock (void * data) { struct mutex_lock *lock = data; + wakeup_fn *fn = lock->wakeup; - unlock(lock); + __unlock(lock); + if (fn) + fn(); +} + +void set_wakeup_fn(struct mutex_lock *lck, wakeup_fn *fn) +{ + lock(lck); + lck->wakeup = fn; + __unlock(lck); } diff --git a/libmultipath/lock.h b/libmultipath/lock.h index a170efe..d7b779e 100644 --- a/libmultipath/lock.h +++ b/libmultipath/lock.h @@ -3,8 +3,11 @@ #include +typedef void (wakeup_fn)(void); + struct mutex_lock { pthread_mutex_t mutex; + wakeup_fn *wakeup; }; static inline void lock(struct mutex_lock *a) @@ -12,12 +15,17 @@ static inline void lock(struct mutex_lock *a) pthread_mutex_lock(&a->mutex); } +static inline int trylock(struct mutex_lock *a) +{ + return pthread_mutex_trylock(&a->mutex); +} + static inline int timedlock(struct mutex_lock *a, struct timespec *tmo) { return pthread_mutex_timedlock(&a->mutex, tmo); } -static inline void unlock(struct mutex_lock *a) +static inline void __unlock(struct mutex_lock *a) { pthread_mutex_unlock(&a->mutex); } @@ -25,5 +33,6 @@ static inline void unlock(struct mutex_lock *a) #define lock_cleanup_pop(a) pthread_cleanup_pop(1) void cleanup_lock (void * data); +void set_wakeup_fn(struct mutex_lock *lock, wakeup_fn *fn); #endif /* _LOCK_H */ diff --git a/libmultipath/log.c b/libmultipath/log.c index 10fa32c..45b3ed8 100644 --- a/libmultipath/log.c +++ b/libmultipath/log.c @@ -11,7 +11,6 @@ #include #include -#include "memory.h" #include "log.h" #include "util.h" @@ -44,7 +43,7 @@ static void dump_logarea (void) static int logarea_init (int size) { logdbg(stderr,"enter logarea_init\n"); - la = (struct logarea *)MALLOC(sizeof(struct logarea)); + la = (struct logarea *)calloc(1, sizeof(struct logarea)); if (!la) return 1; @@ -52,23 +51,24 @@ static int logarea_init (int size) if (size < MAX_MSG_SIZE) size = DEFAULT_AREA_SIZE; - la->start = MALLOC(size); + la->start = calloc(1, size); if (!la->start) { - FREE(la); + free(la); + la = NULL; return 1; } - memset(la->start, 0, size); la->empty = 1; la->end = la->start + size; la->head = la->start; la->tail = la->start; - la->buff = MALLOC(MAX_MSG_SIZE + sizeof(struct logmsg)); + la->buff = calloc(1, MAX_MSG_SIZE + sizeof(struct logmsg)); if (!la->buff) { - FREE(la->start); - FREE(la); + free(la->start); + free(la); + la = NULL; return 1; } return 0; @@ -95,9 +95,10 @@ int log_init(char *program_name, int size) static void free_logarea (void) { - FREE(la->start); - FREE(la->buff); - FREE(la); + free(la->start); + free(la->buff); + free(la); + la = NULL; return; } diff --git a/libmultipath/log_pthread.c b/libmultipath/log_pthread.c index 6599210..69b441b 100644 --- a/libmultipath/log_pthread.c +++ b/libmultipath/log_pthread.c @@ -8,8 +8,6 @@ #include #include -#include "memory.h" - #include "log_pthread.h" #include "log.h" #include "lock.h" diff --git a/libmultipath/memory.c b/libmultipath/memory.c deleted file mode 100644 index 7514642..0000000 --- a/libmultipath/memory.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Part: Memory management framework. This framework is used to - * find any memory leak. - * - * Version: $Id: memory.c,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ - * - * Authors: Alexandre Cassen, - * Jan Holmberg, - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Copyright (C) 2001-2005 Alexandre Cassen, - */ - -#include -#include "memory.h" - -/* - * Memory management. in debug mode, - * help finding eventual memory leak. - * Allocation memory types manipulated are : - * - * +type+--------meaning--------+ - * ! 0 ! Free slot ! - * ! 1 ! Overrun ! - * ! 2 ! free null ! - * ! 3 ! realloc null ! - * ! 4 ! Not previus allocated ! - * ! 8 ! Last free list ! - * ! 9 ! Allocated ! - * +----+-----------------------+ - * - * global variable debug bit 9 ( 512 ) used to - * flag some memory error. - * - */ - -#ifdef _DEBUG_ - -typedef struct { - int type; - int line; - char *func; - char *file; - void *ptr; - unsigned long size; - long csum; -} MEMCHECK; - -/* Last free pointers */ -static MEMCHECK free_list[256]; - -static MEMCHECK alloc_list[MAX_ALLOC_LIST]; -static int number_alloc_list = 0; -static int n = 0; /* Alloc list pointer */ -static int f = 0; /* Free list pointer */ - -void * -dbg_malloc(unsigned long size, char *file, char *function, int line) -{ - void *buf; - int i = 0; - long check; - - buf = zalloc(size + sizeof (long)); - - check = 0xa5a5 + size; - *(long *) ((char *) buf + size) = check; - - while (i < number_alloc_list) { - if (alloc_list[i].type == 0) - break; - i++; - } - - if (i == number_alloc_list) - number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = buf; - alloc_list[i].size = size; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].csum = check; - alloc_list[i].type = 9; - - if (debug & 1) - printf("zalloc[%3d:%3d], %p, %4ld at %s, %3d, %s\n", - i, number_alloc_list, buf, size, file, line, - function); - - n++; - return buf; -} - -char * -dbg_strdup(char *str, char *file, char *function, int line) -{ - void *buf; - int i = 0; - long check; - long size; - - size = strlen(str) + 1; - buf = zalloc(size + sizeof (long)); - strcat(buf, str); - - check = 0xa5a5 + size; - *(long *) ((char *) buf + size) = check; - - while (i < number_alloc_list) { - if (alloc_list[i].type == 0) - break; - i++; - } - - if (i == number_alloc_list) - number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = buf; - alloc_list[i].size = size; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].csum = check; - alloc_list[i].type = 9; - - if (debug & 1) - printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n", - i, number_alloc_list, buf, size, file, line, - function); - - n++; - return buf; -} - - - -/* Display a buffer into a HEXA formatted output */ -static void -dump_buffer(char *buff, int count) -{ - int i, j, c; - int printnext = 1; - - if (count % 16) - c = count + (16 - count % 16); - else - c = count; - - for (i = 0; i < c; i++) { - if (printnext) { - printnext--; - printf("%.4x ", i & 0xffff); - } - if (i < count) - printf("%3.2x", buff[i] & 0xff); - else - printf(" "); - if (!((i + 1) % 8)) { - if ((i + 1) % 16) - printf(" -"); - else { - printf(" "); - for (j = i - 15; j <= i; j++) - if (j < count) { - if ((buff[j] & 0xff) >= 0x20 - && (buff[j] & 0xff) <= 0x7e) - printf("%c", - buff[j] & 0xff); - else - printf("."); - } else - printf(" "); - printf("\n"); - printnext = 1; - } - } - } -} - -int -dbg_free(void *buffer, char *file, char *function, int line) -{ - int i = 0; - void *buf; - - /* If nullpointer remember */ - if (buffer == NULL) { - i = number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = buffer; - alloc_list[i].size = 0; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].type = 2; - if (debug & 1) - printf("free NULL in %s, %3d, %s\n", file, - line, function); - - debug |= 512; /* Memory Error detect */ - - return n; - } else - buf = buffer; - - while (i < number_alloc_list) { - if (alloc_list[i].type == 9 && alloc_list[i].ptr == buf) { - if (* - ((long *) ((char *) alloc_list[i].ptr + - alloc_list[i].size)) == - alloc_list[i].csum) - alloc_list[i].type = 0; /* Release */ - else { - alloc_list[i].type = 1; /* Overrun */ - if (debug & 1) { - printf("free corrupt, buffer overrun [%3d:%3d], %p, %4ld at %s, %3d, %s\n", - i, number_alloc_list, - buf, alloc_list[i].size, file, - line, function); - dump_buffer(alloc_list[i].ptr, - alloc_list[i].size + sizeof (long)); - printf("Check_sum\n"); - dump_buffer((char *) &alloc_list[i].csum, - sizeof(long)); - - debug |= 512; /* Memory Error detect */ - } - } - break; - } - i++; - } - - /* Not found */ - if (i == number_alloc_list) { - printf("Free ERROR %p\n", buffer); - number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = buf; - alloc_list[i].size = 0; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].type = 4; - debug |= 512; - - return n; - } - - if (buffer != NULL) - xfree(buffer); - - if (debug & 1) - printf("free [%3d:%3d], %p, %4ld at %s, %3d, %s\n", - i, number_alloc_list, buf, - alloc_list[i].size, file, line, function); - - free_list[f].file = file; - free_list[f].line = line; - free_list[f].func = function; - free_list[f].ptr = buffer; - free_list[f].type = 8; - free_list[f].csum = i; /* Using this field for row id */ - - f++; - f &= 255; - n--; - - return n; -} - -void -dbg_free_final(char *banner) -{ - unsigned int sum = 0, overrun = 0, badptr = 0; - int i, j; - i = 0; - - printf("\n---[ Memory dump for (%s)]---\n\n", banner); - - while (i < number_alloc_list) { - switch (alloc_list[i].type) { - case 3: - badptr++; - printf - ("null pointer to realloc(nil,%ld)! at %s, %3d, %s\n", - alloc_list[i].size, alloc_list[i].file, - alloc_list[i].line, alloc_list[i].func); - break; - case 4: - badptr++; - printf - ("pointer not found in table to free(%p) [%3d:%3d], at %s, %3d, %s\n", - alloc_list[i].ptr, i, number_alloc_list, - alloc_list[i].file, alloc_list[i].line, - alloc_list[i].func); - for (j = 0; j < 256; j++) - if (free_list[j].ptr == alloc_list[i].ptr) - if (free_list[j].type == 8) - printf - (" -> pointer already released at [%3d:%3d], at %s, %3d, %s\n", - (int) free_list[j].csum, - number_alloc_list, - free_list[j].file, - free_list[j].line, - free_list[j].func); - break; - case 2: - badptr++; - printf("null pointer to free(nil)! at %s, %3d, %s\n", - alloc_list[i].file, alloc_list[i].line, - alloc_list[i].func); - break; - case 1: - overrun++; - printf("%p [%3d:%3d], %4ld buffer overrun!:\n", - alloc_list[i].ptr, i, number_alloc_list, - alloc_list[i].size); - printf(" --> source of malloc: %s, %3d, %s\n", - alloc_list[i].file, alloc_list[i].line, - alloc_list[i].func); - break; - case 9: - sum += alloc_list[i].size; - printf("%p [%3d:%3d], %4ld not released!:\n", - alloc_list[i].ptr, i, number_alloc_list, - alloc_list[i].size); - printf(" --> source of malloc: %s, %3d, %s\n", - alloc_list[i].file, alloc_list[i].line, - alloc_list[i].func); - break; - } - i++; - } - - printf("\n\n---[ Memory dump summary for (%s) ]---\n", banner); - printf("Total number of bytes not freed...: %d\n", sum); - printf("Number of entries not freed.......: %d\n", n); - printf("Maximum allocated entries.........: %d\n", number_alloc_list); - printf("Number of bad entries.............: %d\n", badptr); - printf("Number of buffer overrun..........: %d\n\n", overrun); - - if (sum || n || badptr || overrun) - printf("=> Program seems to have some memory problem !!!\n\n"); - else - printf("=> Program seems to be memory allocation safe...\n\n"); -} - -void * -dbg_realloc(void *buffer, unsigned long size, char *file, char *function, - int line) -{ - int i = 0; - void *buf, *buf2; - long check; - - if (buffer == NULL) { - printf("realloc %p %s, %3d %s\n", buffer, file, line, function); - i = number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = NULL; - alloc_list[i].size = 0; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].type = 3; - return dbg_malloc(size, file, function, line); - } - - buf = buffer; - - while (i < number_alloc_list) { - if (alloc_list[i].ptr == buf) { - buf = alloc_list[i].ptr; - break; - } - i++; - } - - /* not found */ - if (i == number_alloc_list) { - printf("realloc ERROR no matching zalloc %p \n", buffer); - number_alloc_list++; - - assert(number_alloc_list < MAX_ALLOC_LIST); - - alloc_list[i].ptr = buf; - alloc_list[i].size = 0; - alloc_list[i].file = file; - alloc_list[i].func = function; - alloc_list[i].line = line; - alloc_list[i].type = 9; - debug |= 512; /* Memory Error detect */ - return NULL; - } - - buf2 = ((char *) buf) + alloc_list[i].size; - - if (*(long *) (buf2) != alloc_list[i].csum) { - alloc_list[i].type = 1; - debug |= 512; /* Memory Error detect */ - } - buf = realloc(buffer, size + sizeof (long)); - - check = 0xa5a5 + size; - *(long *) ((char *) buf + size) = check; - alloc_list[i].csum = check; - - if (debug & 1) - printf("realloc [%3d:%3d] %p, %4ld %s %d %s -> %p %4ld %s %d %s\n", - i, number_alloc_list, alloc_list[i].ptr, - alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func, - buf, size, file, line, function); - - alloc_list[i].ptr = buf; - alloc_list[i].size = size; - alloc_list[i].file = file; - alloc_list[i].line = line; - alloc_list[i].func = function; - - return buf; -} - -#endif diff --git a/libmultipath/memory.h b/libmultipath/memory.h deleted file mode 100644 index a3c478e..0000000 --- a/libmultipath/memory.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Part: memory.c include file. - * - * Version: $Id: memory.h,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ - * - * Authors: Alexandre Cassen, - * Jan Holmberg, - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Copyright (C) 2001-2005 Alexandre Cassen, - */ - -#ifndef _MEMORY_H -#define _MEMORY_H - -/* system includes */ -#include -#include -#include -#include - -/* Local defines */ -#ifdef _DEBUG_ - -int debug; - -#define MAX_ALLOC_LIST 2048 - -#define MALLOC(n) ( dbg_malloc((n), \ - (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) -#define FREE(b) ( dbg_free((b), \ - (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) -#define REALLOC(b,n) ( dbg_realloc((b), (n), \ - (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) -#define STRDUP(n) ( dbg_strdup((n), \ - (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) - -/* Memory debug prototypes defs */ -extern void *dbg_malloc(unsigned long, char *, char *, int); -extern int dbg_free(void *, char *, char *, int); -extern void *dbg_realloc(void *, unsigned long, char *, char *, int); -extern char *dbg_strdup(char *, char *, char *, int); -extern void dbg_free_final(char *); - -#else - -#define MALLOC(n) (calloc(1,(n))) -#define FREE(p) do { free(p); p = NULL; } while(0) -#define REALLOC(p,n) (realloc((p),(n))) -#define STRDUP(n) (strdup(n)) - -#endif - -/* Common defines */ -#define FREE_PTR(P) if((P)) FREE((P)); - -#endif diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 8ca91bf..014d9b8 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -23,7 +23,6 @@ #include "vector.h" #include "config.h" #include "parser.h" -#include "memory.h" #include "debug.h" #include "strbuf.h" @@ -33,19 +32,19 @@ static int line_nr; int keyword_alloc(vector keywords, char *string, - int (*handler) (struct config *, vector), + handler_fn *handler, print_fn *print, int unique) { struct keyword *keyword; - keyword = (struct keyword *) MALLOC(sizeof (struct keyword)); + keyword = (struct keyword *)calloc(1, sizeof (struct keyword)); if (!keyword) return 1; if (!vector_alloc_slot(keywords)) { - FREE(keyword); + free(keyword); return 1; } keyword->string = string; @@ -72,7 +71,7 @@ install_sublevel_end(void) int _install_keyword(vector keywords, char *string, - int (*handler) (struct config *, vector), + handler_fn *handler, print_fn *print, int unique) { @@ -115,7 +114,7 @@ free_keywords(vector keywords) keyword = VECTOR_SLOT(keywords, i); if (keyword->sub) free_keywords(keyword->sub); - FREE(keyword); + free(keyword); } vector_free(keywords); } @@ -245,7 +244,7 @@ alloc_strvec(char *string) start = cp; if (*cp == '"' && !(in_string && *(cp + 1) == '"')) { cp++; - token = MALLOC(sizeof(quote_marker)); + token = calloc(1, sizeof(quote_marker)); if (!token) goto out; @@ -256,7 +255,7 @@ alloc_strvec(char *string) else in_string = 1; } else if (!in_string && (*cp == '{' || *cp == '}')) { - token = MALLOC(2); + token = malloc(2); if (!token) goto out; @@ -283,7 +282,7 @@ alloc_strvec(char *string) } strlen = cp - start; - token = MALLOC(strlen + 1); + token = calloc(1, strlen + 1); if (!token) goto out; @@ -350,7 +349,7 @@ set_value(vector strvec) (char *)VECTOR_SLOT(strvec, 0)); return NULL; } - alloc = MALLOC(sizeof (char) * (size + 1)); + alloc = calloc(1, sizeof (char) * (size + 1)); if (alloc) memcpy(alloc, str, size); else @@ -358,7 +357,7 @@ set_value(vector strvec) return alloc; } /* Even empty quotes counts as a value (An empty string) */ - alloc = (char *) MALLOC(sizeof (char)); + alloc = (char *)calloc(1, sizeof (char)); if (!alloc) goto oom; for (i = 2; i < VECTOR_SIZE(strvec); i++) { @@ -375,9 +374,9 @@ set_value(vector strvec) /* The first +1 is for the NULL byte. The rest are for the * spaces between words */ len += strlen(str) + 1; - alloc = REALLOC(alloc, sizeof (char) * len); + alloc = realloc(alloc, sizeof (char) * len); if (!alloc) { - FREE(tmp); + free(tmp); goto oom; } if (*alloc != '\0') @@ -504,7 +503,7 @@ validate_config_strvec(vector strvec, const char *file) static int process_stream(struct config *conf, FILE *stream, vector keywords, - const char *file) + const char *section, const char *file) { int i; int r = 0, t; @@ -518,7 +517,7 @@ process_stream(struct config *conf, FILE *stream, vector keywords, if (!uniques) return 1; - buf = MALLOC(MAXBUF); + buf = calloc(1, MAXBUF); if (!buf) { vector_free(uniques); @@ -558,32 +557,39 @@ process_stream(struct config *conf, FILE *stream, vector keywords, goto out; } if (keyword->handler) { - t = (*keyword->handler) (conf, strvec); + t = keyword->handler(conf, strvec, file, + line_nr); r += t; if (t) - condlog(1, "multipath.conf +%d, parsing failed: %s", - line_nr, buf); + condlog(1, "%s line %d, parsing failed: %s", + file, line_nr, buf); } if (keyword->sub) { kw_level++; r += process_stream(conf, stream, - keyword->sub, file); + keyword->sub, + keyword->string, + file); kw_level--; } break; } } - if (i >= VECTOR_SIZE(keywords)) - condlog(1, "%s line %d, invalid keyword: %s", - file, line_nr, str); - + if (i >= VECTOR_SIZE(keywords)) { + if (section) + condlog(1, "%s line %d, invalid keyword in the %s section: %s", + file, line_nr, section, str); + else + condlog(1, "%s line %d, invalid keyword: %s", + file, line_nr, str); + } free_strvec(strvec); } if (kw_level == 1) condlog(1, "missing '%s' at end of %s", EOB, file); out: - FREE(buf); + free(buf); free_uniques(uniques); return r; } @@ -608,7 +614,7 @@ process_file(struct config *conf, const char *file) /* Stream handling */ line_nr = 0; - r = process_stream(conf, stream, conf->keywords, file); + r = process_stream(conf, stream, conf->keywords, NULL, file); fclose(stream); //free_keywords(keywords); diff --git a/libmultipath/parser.h b/libmultipath/parser.h index b43d46f..11ea227 100644 --- a/libmultipath/parser.h +++ b/libmultipath/parser.h @@ -43,10 +43,11 @@ struct strbuf; /* keyword definition */ typedef int print_fn(struct config *, struct strbuf *, const void *); +typedef int handler_fn(struct config *, vector, const char *file, int line_nr); struct keyword { char *string; - int (*handler) (struct config *, vector); + handler_fn *handler; print_fn *print; vector sub; int unique; @@ -62,18 +63,14 @@ struct keyword { for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++) /* Prototypes */ -extern int keyword_alloc(vector keywords, char *string, - int (*handler) (struct config *, vector), - print_fn *print, - int unique); +extern int keyword_alloc(vector keywords, char *string, handler_fn *handler, + print_fn *print, int unique); #define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1) extern void install_sublevel(void); extern void install_sublevel_end(void); -extern int _install_keyword(vector keywords, char *string, - int (*handler) (struct config *, vector), - print_fn *print, - int unique); +extern int _install_keyword(vector keywords, char *string, handler_fn *handler, + print_fn *print, int unique); #define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1) #define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0) extern void dump_keywords(vector keydump, int level); diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index 0e55109..10b44d3 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -8,7 +8,6 @@ #include "checkers.h" #include "util.h" -#include "memory.h" #include "vector.h" #include "structs.h" #include "pgpolicies.h" @@ -244,12 +243,12 @@ int group_by_match(struct multipath * mp, vector paths, } } } - FREE(bitmap); + free(bitmap); return 0; out2: free_pathgroup(pgp, KEEP_PATHS); out1: - FREE(bitmap); + free(bitmap); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; diff --git a/libmultipath/print.c b/libmultipath/print.c index 2fb9f4e..221b515 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -100,6 +100,24 @@ #define PROGRESS_LEN 10 +struct path_data { + char wildcard; + char * header; + int (*snprint)(struct strbuf *, const struct path * pp); +}; + +struct multipath_data { + char wildcard; + char * header; + int (*snprint)(struct strbuf *, const struct multipath * mpp); +}; + +struct pathgroup_data { + char wildcard; + char * header; + int (*snprint)(struct strbuf *, const struct pathgroup * pgp); +}; + #define MAX(x,y) (((x) > (y)) ? (x) : (y)) #define MIN(x,y) (((x) > (y)) ? (y) : (x)) /* @@ -504,6 +522,26 @@ snprint_dm_path_state (struct strbuf *buff, const struct path * pp) } } +static int snprint_initialized(struct strbuf *buff, const struct path * pp) +{ + static const char *init_state_name[] = { + [INIT_NEW] = "new", + [INIT_FAILED] = "failed", + [INIT_MISSING_UDEV] = "udev-missing", + [INIT_REQUESTED_UDEV] = "udev-requested", + [INIT_OK] = "ok", + [INIT_REMOVED] = "removed", + [INIT_PARTIAL] = "partial", + }; + const char *str; + + if (pp->initialized < INIT_NEW || pp->initialized >= __INIT_LAST) + str = "undef"; + else + str = init_state_name[pp->initialized]; + return append_strbuf_str(buff, str); +} + static int snprint_vpr (struct strbuf *buff, const struct path * pp) { @@ -735,7 +773,7 @@ snprint_path_protocol(struct strbuf *buff, const struct path * pp) } } -int +static int snprint_path_marginal(struct strbuf *buff, const struct path * pp) { if (pp->marginal) @@ -751,92 +789,91 @@ snprint_path_vpd_data(struct strbuf *buff, const struct path * pp) return append_strbuf_str(buff, "[undef]"); } -struct multipath_data mpd[] = { - {'n', "name", 0, snprint_name}, - {'w', "uuid", 0, snprint_multipath_uuid}, - {'d', "sysfs", 0, snprint_sysfs}, - {'F', "failback", 0, snprint_failback}, - {'Q', "queueing", 0, snprint_queueing}, - {'N', "paths", 0, snprint_nb_paths}, - {'r', "write_prot", 0, snprint_ro}, - {'t', "dm-st", 0, snprint_dm_map_state}, - {'S', "size", 0, snprint_multipath_size}, - {'f', "features", 0, snprint_features}, - {'x', "failures", 0, snprint_map_failures}, - {'h', "hwhandler", 0, snprint_hwhandler}, - {'A', "action", 0, snprint_action}, - {'0', "path_faults", 0, snprint_path_faults}, - {'1', "switch_grp", 0, snprint_switch_grp}, - {'2', "map_loads", 0, snprint_map_loads}, - {'3', "total_q_time", 0, snprint_total_q_time}, - {'4', "q_timeouts", 0, snprint_q_timeouts}, - {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, - {'v', "vend", 0, snprint_multipath_vend}, - {'p', "prod", 0, snprint_multipath_prod}, - {'e', "rev", 0, snprint_multipath_rev}, - {'G', "foreign", 0, snprint_multipath_foreign}, - {'g', "vpd page data", 0, snprint_multipath_vpd_data}, - {0, NULL, 0 , NULL} +static const struct multipath_data mpd[] = { + {'n', "name", snprint_name}, + {'w', "uuid", snprint_multipath_uuid}, + {'d', "sysfs", snprint_sysfs}, + {'F', "failback", snprint_failback}, + {'Q', "queueing", snprint_queueing}, + {'N', "paths", snprint_nb_paths}, + {'r', "write_prot", snprint_ro}, + {'t', "dm-st", snprint_dm_map_state}, + {'S', "size", snprint_multipath_size}, + {'f', "features", snprint_features}, + {'x', "failures", snprint_map_failures}, + {'h', "hwhandler", snprint_hwhandler}, + {'A', "action", snprint_action}, + {'0', "path_faults", snprint_path_faults}, + {'1', "switch_grp", snprint_switch_grp}, + {'2', "map_loads", snprint_map_loads}, + {'3', "total_q_time", snprint_total_q_time}, + {'4', "q_timeouts", snprint_q_timeouts}, + {'s', "vend/prod/rev", snprint_multipath_vpr}, + {'v', "vend", snprint_multipath_vend}, + {'p', "prod", snprint_multipath_prod}, + {'e', "rev", snprint_multipath_rev}, + {'G', "foreign", snprint_multipath_foreign}, + {'g', "vpd page data", snprint_multipath_vpd_data}, }; -struct path_data pd[] = { - {'w', "uuid", 0, snprint_path_uuid}, - {'i', "hcil", 0, snprint_hcil}, - {'d', "dev", 0, snprint_dev}, - {'D', "dev_t", 0, snprint_dev_t}, - {'t', "dm_st", 0, snprint_dm_path_state}, - {'o', "dev_st", 0, snprint_offline}, - {'T', "chk_st", 0, snprint_chk_state}, - {'s', "vend/prod/rev", 0, snprint_vpr}, - {'c', "checker", 0, snprint_path_checker}, - {'C', "next_check", 0, snprint_next_check}, - {'p', "pri", 0, snprint_pri}, - {'S', "size", 0, snprint_path_size}, - {'z', "serial", 0, snprint_path_serial}, - {'M', "marginal_st", 0, snprint_path_marginal}, - {'m', "multipath", 0, snprint_path_mpp}, - {'N', "host WWNN", 0, snprint_host_wwnn}, - {'n', "target WWNN", 0, snprint_tgt_wwnn}, - {'R', "host WWPN", 0, snprint_host_wwpn}, - {'r', "target WWPN", 0, snprint_tgt_wwpn}, - {'a', "host adapter", 0, snprint_host_adapter}, - {'G', "foreign", 0, snprint_path_foreign}, - {'g', "vpd page data", 0, snprint_path_vpd_data}, - {'0', "failures", 0, snprint_path_failures}, - {'P', "protocol", 0, snprint_path_protocol}, - {0, NULL, 0 , NULL} +static const struct path_data pd[] = { + {'w', "uuid", snprint_path_uuid}, + {'i', "hcil", snprint_hcil}, + {'d', "dev", snprint_dev}, + {'D', "dev_t", snprint_dev_t}, + {'t', "dm_st", snprint_dm_path_state}, + {'o', "dev_st", snprint_offline}, + {'T', "chk_st", snprint_chk_state}, + {'s', "vend/prod/rev", snprint_vpr}, + {'c', "checker", snprint_path_checker}, + {'C', "next_check", snprint_next_check}, + {'p', "pri", snprint_pri}, + {'S', "size", snprint_path_size}, + {'z', "serial", snprint_path_serial}, + {'M', "marginal_st", snprint_path_marginal}, + {'m', "multipath", snprint_path_mpp}, + {'N', "host WWNN", snprint_host_wwnn}, + {'n', "target WWNN", snprint_tgt_wwnn}, + {'R', "host WWPN", snprint_host_wwpn}, + {'r', "target WWPN", snprint_tgt_wwpn}, + {'a', "host adapter", snprint_host_adapter}, + {'G', "foreign", snprint_path_foreign}, + {'g', "vpd page data", snprint_path_vpd_data}, + {'0', "failures", snprint_path_failures}, + {'P', "protocol", snprint_path_protocol}, + {'I', "init_st", snprint_initialized}, }; -struct pathgroup_data pgd[] = { - {'s', "selector", 0, snprint_pg_selector}, - {'p', "pri", 0, snprint_pg_pri}, - {'t', "dm_st", 0, snprint_pg_state}, - {'M', "marginal_st", 0, snprint_pg_marginal}, - {0, NULL, 0 , NULL} +static const struct pathgroup_data pgd[] = { + {'s', "selector", snprint_pg_selector}, + {'p', "pri", snprint_pg_pri}, + {'t', "dm_st", snprint_pg_state}, + {'M', "marginal_st", snprint_pg_marginal}, }; int snprint_wildcards(struct strbuf *buff) { int initial_len = get_strbuf_len(buff); - int i, rc; + unsigned int i; + int rc; if ((rc = append_strbuf_str(buff, "multipath format wildcards:\n")) < 0) return rc; - for (i = 0; mpd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(mpd); i++) if ((rc = print_strbuf(buff, "%%%c %s\n", mpd[i].wildcard, mpd[i].header)) < 0) return rc; if ((rc = append_strbuf_str(buff, "\npath format wildcards:\n")) < 0) return rc; - for (i = 0; pd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(pd); i++) if ((rc = print_strbuf(buff, "%%%c %s\n", pd[i].wildcard, pd[i].header)) < 0) return rc; if ((rc = append_strbuf_str(buff, "\npathgroup format wildcards:\n")) < 0) return rc; - for (i = 0; pgd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(pgd); i++) if ((rc = print_strbuf(buff, "%%%c %s\n", pgd[i].wildcard, pgd[i].header)) < 0) return rc; @@ -844,18 +881,22 @@ int snprint_wildcards(struct strbuf *buff) return get_strbuf_len(buff) - initial_len; } -void -get_path_layout(vector pathvec, int header) +fieldwidth_t *alloc_path_layout(void) { + return calloc(ARRAY_SIZE(pd), sizeof(fieldwidth_t)); +} + +void get_path_layout(vector pathvec, int header, fieldwidth_t *width) { vector gpvec = vector_convert(NULL, pathvec, struct path, dm_path_to_gen); _get_path_layout(gpvec, - header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO); + header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO, + width); vector_free(gpvec); } static void -reset_width(unsigned int *width, enum layout_reset reset, const char *header) +reset_width(fieldwidth_t *width, enum layout_reset reset, const char *header) { switch (reset) { case LAYOUT_RESET_HEADER: @@ -870,158 +911,162 @@ reset_width(unsigned int *width, enum layout_reset reset, const char *header) } } -void -_get_path_layout (const struct _vector *gpvec, enum layout_reset reset) +void _get_path_layout (const struct _vector *gpvec, enum layout_reset reset, + fieldwidth_t *width) { - int i, j; + unsigned int i, j; const struct gen_path *gp; - for (j = 0; pd[j].header; j++) { + if (width == NULL) + return; + + for (j = 0; j < ARRAY_SIZE(pd); j++) { STRBUF_ON_STACK(buff); - reset_width(&pd[j].width, reset, pd[j].header); + reset_width(&width[j], reset, pd[j].header); if (gpvec == NULL) continue; vector_foreach_slot (gpvec, gp, i) { gp->ops->snprint(gp, &buff, pd[j].wildcard); - pd[j].width = MAX(pd[j].width, get_strbuf_len(&buff)); + width[j] = MAX(width[j], + MIN(get_strbuf_len(&buff), MAX_FIELD_WIDTH)); truncate_strbuf(&buff, 0); } } } -static void -reset_multipath_layout (void) -{ - int i; +fieldwidth_t *alloc_multipath_layout(void) { - for (i = 0; mpd[i].header; i++) - mpd[i].width = 0; + return calloc(ARRAY_SIZE(mpd), sizeof(fieldwidth_t)); } -void -get_multipath_layout (vector mpvec, int header) { +void get_multipath_layout (vector mpvec, int header, fieldwidth_t *width) { vector gmvec = vector_convert(NULL, mpvec, struct multipath, dm_multipath_to_gen); _get_multipath_layout(gmvec, - header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO); + header ? LAYOUT_RESET_HEADER : LAYOUT_RESET_ZERO, + width); vector_free(gmvec); } void -_get_multipath_layout (const struct _vector *gmvec, - enum layout_reset reset) +_get_multipath_layout (const struct _vector *gmvec, enum layout_reset reset, + fieldwidth_t *width) { - int i, j; + unsigned int i, j; const struct gen_multipath * gm; - for (j = 0; mpd[j].header; j++) { + if (width == NULL) + return; + for (j = 0; j < ARRAY_SIZE(mpd); j++) { STRBUF_ON_STACK(buff); - reset_width(&mpd[j].width, reset, mpd[j].header); + reset_width(&width[j], reset, mpd[j].header); if (gmvec == NULL) continue; vector_foreach_slot (gmvec, gm, i) { gm->ops->snprint(gm, &buff, mpd[j].wildcard); - mpd[j].width = MAX(mpd[j].width, get_strbuf_len(&buff)); + width[j] = MAX(width[j], + MIN(get_strbuf_len(&buff), MAX_FIELD_WIDTH)); truncate_strbuf(&buff, 0); } - condlog(4, "%s: width %d", mpd[j].header, mpd[j].width); + condlog(4, "%s: width %d", mpd[j].header, width[j]); } } -static struct multipath_data * -mpd_lookup(char wildcard) +static int mpd_lookup(char wildcard) { - int i; + unsigned int i; - for (i = 0; mpd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(mpd); i++) if (mpd[i].wildcard == wildcard) - return &mpd[i]; + return i; - return NULL; + return -1; } int snprint_multipath_attr(const struct gen_multipath* gm, struct strbuf *buf, char wildcard) { const struct multipath *mpp = gen_multipath_to_dm(gm); - struct multipath_data *mpd = mpd_lookup(wildcard); + int i = mpd_lookup(wildcard); - if (mpd == NULL) + if (i == -1) return 0; - return mpd->snprint(buf, mpp); + return mpd[i].snprint(buf, mpp); } -static struct path_data * -pd_lookup(char wildcard) +static int pd_lookup(char wildcard) { - int i; + unsigned int i; - for (i = 0; pd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(pd); i++) if (pd[i].wildcard == wildcard) - return &pd[i]; + return i; - return NULL; + return -1; } int snprint_path_attr(const struct gen_path* gp, struct strbuf *buf, char wildcard) { const struct path *pp = gen_path_to_dm(gp); - struct path_data *pd = pd_lookup(wildcard); + int i = pd_lookup(wildcard); - if (pd == NULL) + if (i == -1) return 0; - return pd->snprint(buf, pp); + return pd[i].snprint(buf, pp); } -static struct pathgroup_data * -pgd_lookup(char wildcard) +static int pgd_lookup(char wildcard) { - int i; + unsigned int i; - for (i = 0; pgd[i].header; i++) + for (i = 0; i < ARRAY_SIZE(pgd); i++) if (pgd[i].wildcard == wildcard) - return &pgd[i]; + return i; - return NULL; + return -1; } int snprint_pathgroup_attr(const struct gen_pathgroup* gpg, struct strbuf *buf, char wildcard) { const struct pathgroup *pg = gen_pathgroup_to_dm(gpg); - struct pathgroup_data *pdg = pgd_lookup(wildcard); + int i = pgd_lookup(wildcard); - if (pdg == NULL) + if (i == -1) return 0; - return pdg->snprint(buf, pg); + return pgd[i].snprint(buf, pg); } -int snprint_multipath_header(struct strbuf *line, const char *format) +int snprint_multipath_header(struct strbuf *line, const char *format, + const fieldwidth_t *width) { int initial_len = get_strbuf_len(line); const char *f; - struct multipath_data * data; + const struct multipath_data * data; int rc; for (f = strchr(format, '%'); f; f = strchr(++format, '%')) { + int iwc; + if ((rc = __append_strbuf_str(line, format, f - format)) < 0) return rc; format = f + 1; - if (!(data = mpd_lookup(*format))) + if ((iwc = mpd_lookup(*format)) == -1) continue; /* unknown wildcard */ + data = &mpd[iwc]; if ((rc = append_strbuf_str(line, data->header)) < 0) return rc; - else if ((unsigned int)rc < data->width) - if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0) + else if ((unsigned int)rc < width[iwc]) + if ((rc = fill_strbuf(line, ' ', width[iwc] - rc)) < 0) return rc; } @@ -1031,25 +1076,27 @@ int snprint_multipath_header(struct strbuf *line, const char *format) } int _snprint_multipath(const struct gen_multipath *gmp, - struct strbuf *line, const char *format, int pad) + struct strbuf *line, const char *format, + const fieldwidth_t *width) { int initial_len = get_strbuf_len(line); const char *f; - struct multipath_data * data; int rc; for (f = strchr(format, '%'); f; f = strchr(++format, '%')) { + int iwc; + if ((rc = __append_strbuf_str(line, format, f - format)) < 0) return rc; format = f + 1; - if (!(data = mpd_lookup(*format))) + if ((iwc = mpd_lookup(*format)) == -1) continue; /* unknown wildcard */ if ((rc = gmp->ops->snprint(gmp, line, *format)) < 0) return rc; - else if (pad && (unsigned int)rc < data->width) - if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0) + else if (width != NULL && (unsigned int)rc < width[iwc]) + if ((rc = fill_strbuf(line, ' ', width[iwc] - rc)) < 0) return rc; } @@ -1058,25 +1105,29 @@ int _snprint_multipath(const struct gen_multipath *gmp, return get_strbuf_len(line) - initial_len; } -int snprint_path_header(struct strbuf *line, const char *format) +int snprint_path_header(struct strbuf *line, const char *format, + const fieldwidth_t *width) { int initial_len = get_strbuf_len(line); const char *f; - struct path_data *data; + const struct path_data *data; int rc; for (f = strchr(format, '%'); f; f = strchr(++format, '%')) { + int iwc; + if ((rc = __append_strbuf_str(line, format, f - format)) < 0) return rc; format = f + 1; - if (!(data = pd_lookup(*format))) + if ((iwc = pd_lookup(*format)) == -1) continue; /* unknown wildcard */ + data = &pd[iwc]; if ((rc = append_strbuf_str(line, data->header)) < 0) return rc; - else if ((unsigned int)rc < data->width) - if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0) + else if ((unsigned int)rc < width[iwc]) + if ((rc = fill_strbuf(line, ' ', width[iwc] - rc)) < 0) return rc; } @@ -1086,25 +1137,26 @@ int snprint_path_header(struct strbuf *line, const char *format) } int _snprint_path(const struct gen_path *gp, struct strbuf *line, - const char *format, int pad) + const char *format, const fieldwidth_t *width) { int initial_len = get_strbuf_len(line); const char *f; - struct path_data * data; int rc; for (f = strchr(format, '%'); f; f = strchr(++format, '%')) { + int iwc; + if ((rc = __append_strbuf_str(line, format, f - format)) < 0) return rc; format = f + 1; - if (!(data = pd_lookup(*format))) + if ((iwc = pd_lookup(*format)) == -1) continue; /* unknown wildcard */ if ((rc = gp->ops->snprint(gp, line, *format)) < 0) return rc; - else if (pad && (unsigned int)rc < data->width) - if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0) + else if (width != NULL && (unsigned int)rc < width[iwc]) + if ((rc = fill_strbuf(line, ' ', width[iwc] - rc)) < 0) return rc; } @@ -1118,7 +1170,6 @@ int _snprint_pathgroup(const struct gen_pathgroup *ggp, struct strbuf *line, { int initial_len = get_strbuf_len(line); const char *f; - struct pathgroup_data *data; int rc; for (f = strchr(format, '%'); f; f = strchr(++format, '%')) { @@ -1126,14 +1177,9 @@ int _snprint_pathgroup(const struct gen_pathgroup *ggp, struct strbuf *line, return rc; format = f + 1; - if (!(data = pgd_lookup(*format))) - continue; /* unknown wildcard */ if ((rc = ggp->ops->snprint(ggp, line, *format)) < 0) return rc; - else if ((unsigned int)rc < data->width) - if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0) - return rc; } if ((rc = print_strbuf(line, "%s\n", format)) < 0) @@ -1147,8 +1193,26 @@ int _snprint_pathgroup(const struct gen_pathgroup *ggp, struct strbuf *line, void _print_multipath_topology(const struct gen_multipath *gmp, int verbosity) { STRBUF_ON_STACK(buff); + fieldwidth_t *p_width __attribute__((cleanup(cleanup_ucharp))) = NULL; + const struct gen_pathgroup *gpg; + const struct _vector *pgvec, *pathvec; + int j; + + p_width = alloc_path_layout(); + pgvec = gmp->ops->get_pathgroups(gmp); - _snprint_multipath_topology(gmp, &buff, verbosity); + if (pgvec != NULL) { + vector_foreach_slot (pgvec, gpg, j) { + pathvec = gpg->ops->get_paths(gpg); + if (pathvec == NULL) + continue; + _get_path_layout(pathvec, LAYOUT_RESET_NOT, p_width); + gpg->ops->rel_paths(gpg, pathvec); + } + gmp->ops->rel_pathgroups(gmp, pgvec); + } + + _snprint_multipath_topology(gmp, &buff, verbosity, p_width); printf("%s", get_strbuf_str(&buff)); } @@ -1168,21 +1232,24 @@ int snprint_multipath_style(const struct gen_multipath *gmp, } int _snprint_multipath_topology(const struct gen_multipath *gmp, - struct strbuf *buff, int verbosity) + struct strbuf *buff, int verbosity, + const fieldwidth_t *p_width) { int j, i, rc; const struct _vector *pgvec; const struct gen_pathgroup *gpg; STRBUF_ON_STACK(style); size_t initial_len = get_strbuf_len(buff); + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; if (verbosity <= 0) return 0; - reset_multipath_layout(); + if ((width = alloc_multipath_layout()) == NULL) + return -ENOMEM; if (verbosity == 1) - return _snprint_multipath(gmp, buff, "%n", 1); + return _snprint_multipath(gmp, buff, "%n", width); if(isatty(1) && (rc = print_strbuf(&style, "%c[%dm", 0x1B, 1)) < 0) /* bold on */ @@ -1193,8 +1260,8 @@ int _snprint_multipath_topology(const struct gen_multipath *gmp, (rc = print_strbuf(&style, "%c[%dm", 0x1B, 0)) < 0) /* bold off */ return rc; - if ((rc = _snprint_multipath(gmp, buff, get_strbuf_str(&style), 1)) < 0 - || (rc = _snprint_multipath(gmp, buff, PRINT_MAP_PROPS, 1)) < 0) + if ((rc = _snprint_multipath(gmp, buff, get_strbuf_str(&style), width)) < 0 + || (rc = _snprint_multipath(gmp, buff, PRINT_MAP_PROPS, width)) < 0) return rc; pgvec = gmp->ops->get_pathgroups(gmp); @@ -1221,7 +1288,7 @@ int _snprint_multipath_topology(const struct gen_multipath *gmp, i + 1 == VECTOR_SIZE(pathvec) ? '`': '|')) < 0 || (rc = _snprint_path(gp, buff, - PRINT_PATH_INDENT, 1)) < 0) + PRINT_PATH_INDENT, p_width)) < 0) return rc; } gpg->ops->rel_paths(gpg, pathvec); @@ -1756,24 +1823,36 @@ static int snprint_blacklist_except(const struct config *conf, return get_strbuf_len(buff) - initial_len; } -char *snprint_config(const struct config *conf, int *len, +int __snprint_config(const struct config *conf, struct strbuf *buff, const struct _vector *hwtable, const struct _vector *mpvec) { - STRBUF_ON_STACK(buff); - char *reply; int rc; - if ((rc = snprint_defaults(conf, &buff)) < 0 || - (rc = snprint_blacklist(conf, &buff)) < 0 || - (rc = snprint_blacklist_except(conf, &buff)) < 0 || - (rc = snprint_hwtable(conf, &buff, + if ((rc = snprint_defaults(conf, buff)) < 0 || + (rc = snprint_blacklist(conf, buff)) < 0 || + (rc = snprint_blacklist_except(conf, buff)) < 0 || + (rc = snprint_hwtable(conf, buff, hwtable ? hwtable : conf->hwtable)) < 0 || - (rc = snprint_overrides(conf, &buff, conf->overrides)) < 0) - return NULL; + (rc = snprint_overrides(conf, buff, conf->overrides)) < 0) + return rc; + if (VECTOR_SIZE(conf->mptable) > 0 || (mpvec != NULL && VECTOR_SIZE(mpvec) > 0)) - if ((rc = snprint_mptable(conf, &buff, mpvec)) < 0) - return NULL; + if ((rc = snprint_mptable(conf, buff, mpvec)) < 0) + return rc; + + return 0; +} + +char *snprint_config(const struct config *conf, int *len, + const struct _vector *hwtable, const struct _vector *mpvec) +{ + STRBUF_ON_STACK(buff); + char *reply; + int rc = __snprint_config(conf, &buff, hwtable, mpvec); + + if (rc < 0) + return NULL; if (len) *len = get_strbuf_len(&buff); @@ -1894,6 +1973,7 @@ static void print_all_paths_custo(vector pathvec, int banner, const char *fmt) int i; struct path * pp; STRBUF_ON_STACK(line); + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; if (!VECTOR_SIZE(pathvec)) { if (banner) @@ -1901,14 +1981,17 @@ static void print_all_paths_custo(vector pathvec, int banner, const char *fmt) return; } + if ((width = alloc_path_layout()) == NULL) + return; + get_path_layout(pathvec, 1, width); + if (banner) append_strbuf_str(&line, "===== paths list =====\n"); - get_path_layout(pathvec, 1); - snprint_path_header(&line, fmt); + snprint_path_header(&line, fmt, width); vector_foreach_slot (pathvec, pp, i) - snprint_path(&line, fmt, pp, 1); + snprint_path(&line, fmt, pp, width); printf("%s", get_strbuf_str(&line)); } diff --git a/libmultipath/print.h b/libmultipath/print.h index c6674a5..52f5b25 100644 --- a/libmultipath/print.h +++ b/libmultipath/print.h @@ -9,51 +9,39 @@ struct strbuf; -struct path_data { - char wildcard; - char * header; - unsigned int width; - int (*snprint)(struct strbuf *, const struct path * pp); -}; - -struct multipath_data { - char wildcard; - char * header; - unsigned int width; - int (*snprint)(struct strbuf *, const struct multipath * mpp); -}; - -struct pathgroup_data { - char wildcard; - char * header; - unsigned int width; - int (*snprint)(struct strbuf *, const struct pathgroup * pgp); -}; - enum layout_reset { LAYOUT_RESET_NOT, LAYOUT_RESET_ZERO, LAYOUT_RESET_HEADER, }; -void _get_path_layout (const struct _vector *gpvec, enum layout_reset); -void get_path_layout (vector pathvec, int header); -void _get_multipath_layout (const struct _vector *gmvec, enum layout_reset); -void get_multipath_layout (vector mpvec, int header); -int snprint_path_header(struct strbuf *, const char *); -int snprint_multipath_header(struct strbuf *, const char *); -int _snprint_path (const struct gen_path *, struct strbuf *, const char *, int); -#define snprint_path(buf, fmt, pp, v) \ - _snprint_path(dm_path_to_gen(pp), buf, fmt, v) +/* fieldwidth_t is defined in generic.h */ +fieldwidth_t *alloc_path_layout(void); +void _get_path_layout (const struct _vector *gpvec, enum layout_reset, + fieldwidth_t *width); +void get_path_layout (vector pathvec, int header, fieldwidth_t *width); +fieldwidth_t *alloc_multipath_layout(void); +void _get_multipath_layout (const struct _vector *gmvec, enum layout_reset, + fieldwidth_t *width); +void get_multipath_layout (vector mpvec, int header, fieldwidth_t *width); +int snprint_path_header(struct strbuf *, const char *, const fieldwidth_t *); +int snprint_multipath_header(struct strbuf *, const char *, + const fieldwidth_t *); +int _snprint_path (const struct gen_path *, struct strbuf *, const char *, + const fieldwidth_t *); +#define snprint_path(buf, fmt, pp, w) \ + _snprint_path(dm_path_to_gen(pp), buf, fmt, w) int _snprint_multipath (const struct gen_multipath *, struct strbuf *, - const char *, int); -#define snprint_multipath(buf, fmt, mp, v) \ - _snprint_multipath(dm_multipath_to_gen(mp), buf, fmt, v) + const char *, const fieldwidth_t *); +#define snprint_multipath(buf, fmt, mp, w) \ + _snprint_multipath(dm_multipath_to_gen(mp), buf, fmt, w) int _snprint_multipath_topology (const struct gen_multipath *, struct strbuf *, - int verbosity); -#define snprint_multipath_topology(buf, mpp, v) \ - _snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v) + int verbosity, const fieldwidth_t *); +#define snprint_multipath_topology(buf, mpp, v, w) \ + _snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v, w) int snprint_multipath_topology_json(struct strbuf *, const struct vectors *vecs); +int __snprint_config(const struct config *conf, struct strbuf *buff, + const struct _vector *hwtable, const struct _vector *mpvec); char *snprint_config(const struct config *conf, int *len, const struct _vector *hwtable, const struct _vector *mpvec); diff --git a/libmultipath/prio.c b/libmultipath/prio.c index c92bde7..ef68cd0 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -51,7 +51,7 @@ static struct prio * alloc_prio (void) { struct prio *p; - p = MALLOC(sizeof(struct prio)); + p = calloc(1, sizeof(struct prio)); if (p) { INIT_LIST_HEAD(&p->node); p->refcount = 1; @@ -77,7 +77,7 @@ void free_prio (struct prio * p) p->name, dlerror()); } } - FREE(p); + free(p); } void cleanup_prio(void) diff --git a/libmultipath/prio.h b/libmultipath/prio.h index 26754f7..66c7936 100644 --- a/libmultipath/prio.h +++ b/libmultipath/prio.h @@ -11,7 +11,6 @@ struct path; #include "list.h" -#include "memory.h" #include "defaults.h" /* diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c index 420a2e3..3f9c0e7 100644 --- a/libmultipath/prioritizers/alua_rtpg.c +++ b/libmultipath/prioritizers/alua_rtpg.c @@ -27,7 +27,6 @@ #include "../structs.h" #include "../prio.h" #include "../discovery.h" -#include "../unaligned.h" #include "../debug.h" #include "alua_rtpg.h" @@ -252,12 +251,12 @@ int get_target_port_group(const struct path * pp, unsigned int timeout) { unsigned char *buf; - struct vpd83_data * vpd83; - struct vpd83_dscr * dscr; + const struct vpd83_data * vpd83; + const struct vpd83_dscr * dscr; int rc; int buflen, scsi_buflen; - buflen = 4096; + buflen = VPD_BUFLEN; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" @@ -298,13 +297,13 @@ get_target_port_group(const struct path * pp, unsigned int timeout) rc = -RTPG_NO_TPG_IDENTIFIER; FOR_EACH_VPD83_DSCR(vpd83, dscr) { if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { - struct vpd83_tpg_dscr *p; + const struct vpd83_tpg_dscr *p; if (rc != -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: more " "than one TPG identifier found!"); continue; } - p = (struct vpd83_tpg_dscr *)dscr->data; + p = (const struct vpd83_tpg_dscr *)dscr->data; rc = get_unaligned_be16(p->tpg); } } @@ -377,7 +376,7 @@ get_asymmetric_access_state(const struct path *pp, unsigned int tpg, uint64_t scsi_buflen; int fd = pp->fd; - buflen = 4096; + buflen = VPD_BUFLEN; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" diff --git a/libmultipath/prioritizers/alua_spc3.h b/libmultipath/prioritizers/alua_spc3.h index 7ba2cf4..f0a4bc4 100644 --- a/libmultipath/prioritizers/alua_spc3.h +++ b/libmultipath/prioritizers/alua_spc3.h @@ -14,6 +14,7 @@ */ #ifndef __SPC3_H__ #define __SPC3_H__ +#include "../unaligned.h" /*============================================================================= * Definitions to support the standard inquiry command as defined in SPC-3. @@ -177,7 +178,7 @@ struct vpd83_dscr { } __attribute__((packed)); static inline int -vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) +vpd83_dscr_istype(const struct vpd83_dscr *d, unsigned char type) { return ((d->b1 & 7) == type); } @@ -190,6 +191,38 @@ struct vpd83_data { struct vpd83_dscr data[0]; } __attribute__((packed)); +#define VPD_BUFLEN 4096 + +/* Returns the max byte offset in the VPD page from the start of the page */ +static inline unsigned int vpd83_max_offs(const struct vpd83_data *p) +{ + uint16_t len = get_unaligned_be16(p->length) + 4; + + return len <= VPD_BUFLEN ? len : VPD_BUFLEN; +} + +static inline bool +vpd83_descr_fits(const struct vpd83_dscr *d, const struct vpd83_data *p) +{ + ptrdiff_t max_offs = vpd83_max_offs(p); + ptrdiff_t offs = ((const char *)d - (const char *)p); + + /* make sure we can read d->length */ + if (offs < 0 || offs > max_offs - 4) + return false; + + offs += d->length + 4; + return offs <= max_offs; +} + +static inline const struct vpd83_dscr * +vpd83_next_dscr(const struct vpd83_dscr *d, const struct vpd83_data *p) +{ + ptrdiff_t offs = ((const char *)d - (const char *)p) + d->length + 4; + + return (const struct vpd83_dscr *)((const char *)p + offs); +} + /*----------------------------------------------------------------------------- * This macro should be used to walk through all identification descriptors * defined in the code page 0x83. @@ -199,11 +232,9 @@ struct vpd83_data { */ #define FOR_EACH_VPD83_DSCR(p, d) \ for( \ - d = p->data; \ - (((char *) d) - ((char *) p)) < \ - get_unaligned_be16(p->length); \ - d = (struct vpd83_dscr *) \ - ((char *) d + d->length + 4) \ + d = p->data; \ + vpd83_descr_fits(d, p); \ + d = vpd83_next_dscr(d, p) \ ) /*============================================================================= diff --git a/libmultipath/prioritizers/path_latency.c b/libmultipath/prioritizers/path_latency.c index eeee01e..078226d 100644 --- a/libmultipath/prioritizers/path_latency.c +++ b/libmultipath/prioritizers/path_latency.c @@ -158,7 +158,7 @@ static int get_ionum_and_basenum(char *args, int *ionum, double *basenum) return 0; } - arg = temp = STRDUP(args); + arg = temp = strdup(args); if (!arg) return 0; @@ -185,10 +185,10 @@ static int get_ionum_and_basenum(char *args, int *ionum, double *basenum) if (check_args_valid(*ionum, *basenum) == 0) goto out; - FREE(arg); + free(arg); return 1; out: - FREE(arg); + free(arg); return 0; } diff --git a/libmultipath/prioritizers/weightedpath.c b/libmultipath/prioritizers/weightedpath.c index ea03fc3..561ebb4 100644 --- a/libmultipath/prioritizers/weightedpath.c +++ b/libmultipath/prioritizers/weightedpath.c @@ -29,7 +29,6 @@ #include "weightedpath.h" #include "config.h" #include "structs.h" -#include "memory.h" #include "debug.h" #include #include "structs_vec.h" @@ -75,13 +74,13 @@ int prio_path_weight(struct path *pp, char *prio_args) if (!prio_args) return priority; - arg = temp = STRDUP(prio_args); + arg = strdup(prio_args); + temp = arg; regex = get_next_string(&temp, split_char); /* Return default priority if the argument is not parseable */ if (!regex) { - FREE(arg); return priority; } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index b287667..a842fc3 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -7,7 +7,6 @@ #include "nvme-lib.h" #include "checkers.h" -#include "memory.h" #include "vector.h" #include "structs.h" #include "config.h" @@ -255,7 +254,7 @@ int select_selector(struct config *conf, struct multipath * mp) mp_set_conf(selector); mp_set_default(selector, DEFAULT_SELECTOR); out: - mp->selector = STRDUP(mp->selector); + mp->selector = strdup(mp->selector); condlog(3, "%s: path_selector = \"%s\" %s", mp->alias, mp->selector, origin); return 0; @@ -303,7 +302,7 @@ int select_alias(struct config *conf, struct multipath * mp) const char *origin = NULL; if (mp->mpe && mp->mpe->alias) { - mp->alias = STRDUP(mp->mpe->alias); + mp->alias = strdup(mp->mpe->alias); origin = multipaths_origin; goto out; } @@ -329,7 +328,7 @@ int select_alias(struct config *conf, struct multipath * mp) } out: if (mp->alias == NULL) { - mp->alias = STRDUP(mp->wwid); + mp->alias = strdup(mp->wwid); origin = "(setting: default to WWID)"; } if (mp->alias) @@ -399,7 +398,7 @@ int select_features(struct config *conf, struct multipath *mp) mp_set_conf(features); mp_set_default(features, DEFAULT_FEATURES); out: - mp->features = STRDUP(mp->features); + mp->features = strdup(mp->features); reconcile_features_with_options(mp->alias, &mp->features, &mp->no_path_retry, @@ -477,7 +476,7 @@ out: mp->hwhandler = DEFAULT_HWHANDLER; origin = tpgs_origin; } - mp->hwhandler = STRDUP(mp->hwhandler); + mp->hwhandler = strdup(mp->hwhandler); condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, origin); return 0; @@ -525,7 +524,7 @@ int select_checker(struct config *conf, struct path *pp) ckr_name = RDAC; goto out; } - path_get_tpgs(pp); + (void)path_get_tpgs(pp); if (pp->tpgs != TPGS_NONE && pp->tpgs != TPGS_UNDEF) { ckr_name = TUR; goto out; diff --git a/libmultipath/strbuf.c b/libmultipath/strbuf.c index a24a57d..f654594 100644 --- a/libmultipath/strbuf.c +++ b/libmultipath/strbuf.c @@ -15,6 +15,11 @@ static const char empty_str[] = ""; +char *__get_strbuf_buf(struct strbuf *buf) +{ + return buf->buf; +} + const char *get_strbuf_str(const struct strbuf *buf) { return buf->buf ? buf->buf : empty_str; diff --git a/libmultipath/strbuf.h b/libmultipath/strbuf.h index 5903572..41d7d54 100644 --- a/libmultipath/strbuf.h +++ b/libmultipath/strbuf.h @@ -54,6 +54,20 @@ void free_strbuf(struct strbuf *buf); */ struct strbuf *new_strbuf(void); +/** + * get_strbuf_buf(): retrieve a pointer to the strbuf's buffer + * @param buf: a struct strbuf + * @returns: pointer to the string written to the strbuf so far. + * + * INTERNAL ONLY. + * DANGEROUS: Unlike the return value of get_strbuf_str(), + * this string can be written to, modifying the strbuf's content. + * USE WITH CAUTION. + * If @strbuf was never written to, the function returns NULL. + * The return value of this function must not be free()d. + */ +char *__get_strbuf_buf(struct strbuf *buf); + /** * get_strbuf_str(): retrieve string from strbuf * @param buf: a struct strbuf diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 6e5a103..d1b8aa3 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -8,7 +8,6 @@ #include #include "checkers.h" -#include "memory.h" #include "vector.h" #include "util.h" #include "structs.h" @@ -25,14 +24,14 @@ alloc_adaptergroup(void) { struct adapter_group *agp; - agp = (struct adapter_group *)MALLOC(sizeof(struct adapter_group)); + agp = (struct adapter_group *)calloc(1, sizeof(struct adapter_group)); if (!agp) return NULL; agp->host_groups = vector_alloc(); if (!agp->host_groups) { - FREE(agp); + free(agp); agp = NULL; } return agp; @@ -45,7 +44,7 @@ void free_adaptergroup(vector adapters) vector_foreach_slot(adapters, agp, i) { free_hostgroup(agp->host_groups); - FREE(agp); + free(agp); } vector_free(adapters); } @@ -60,7 +59,7 @@ void free_hostgroup(vector hostgroups) vector_foreach_slot(hostgroups, hgp, i) { vector_free(hgp->paths); - FREE(hgp); + free(hgp); } vector_free(hostgroups); } @@ -70,7 +69,7 @@ alloc_hostgroup(void) { struct host_group *hgp; - hgp = (struct host_group *)MALLOC(sizeof(struct host_group)); + hgp = (struct host_group *)calloc(1, sizeof(struct host_group)); if (!hgp) return NULL; @@ -78,7 +77,7 @@ alloc_hostgroup(void) hgp->paths = vector_alloc(); if (!hgp->paths) { - FREE(hgp); + free(hgp); hgp = NULL; } return hgp; @@ -89,7 +88,7 @@ alloc_path (void) { struct path * pp; - pp = (struct path *)MALLOC(sizeof(struct path)); + pp = (struct path *)calloc(1, sizeof(struct path)); if (pp) { pp->initialized = INIT_NEW; @@ -152,7 +151,7 @@ free_path (struct path * pp) vector_free(pp->hwe); - FREE(pp); + free(pp); } void @@ -176,7 +175,7 @@ alloc_pathgroup (void) { struct pathgroup * pgp; - pgp = (struct pathgroup *)MALLOC(sizeof(struct pathgroup)); + pgp = (struct pathgroup *)calloc(1, sizeof(struct pathgroup)); if (!pgp) return NULL; @@ -184,7 +183,7 @@ alloc_pathgroup (void) pgp->paths = vector_alloc(); if (!pgp->paths) { - FREE(pgp); + free(pgp); return NULL; } @@ -199,7 +198,7 @@ free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths) return; free_pathvec(pgp->paths, free_paths); - FREE(pgp); + free(pgp); } void @@ -222,7 +221,7 @@ alloc_multipath (void) { struct multipath * mpp; - mpp = (struct multipath *)MALLOC(sizeof(struct multipath)); + mpp = (struct multipath *)calloc(1, sizeof(struct multipath)); if (mpp) { mpp->bestpg = 1; @@ -251,17 +250,17 @@ void free_multipath_attributes(struct multipath *mpp) return; if (mpp->selector) { - FREE(mpp->selector); + free(mpp->selector); mpp->selector = NULL; } if (mpp->features) { - FREE(mpp->features); + free(mpp->features); mpp->features = NULL; } if (mpp->hwhandler) { - FREE(mpp->hwhandler); + free(mpp->hwhandler); mpp->hwhandler = NULL; } } @@ -275,12 +274,12 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths) free_multipath_attributes(mpp); if (mpp->alias) { - FREE(mpp->alias); + free(mpp->alias); mpp->alias = NULL; } if (mpp->dmi) { - FREE(mpp->dmi); + free(mpp->dmi); mpp->dmi = NULL; } @@ -305,8 +304,8 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths) vector_free(mpp->hwe); mpp->hwe = NULL; } - FREE_PTR(mpp->mpcontext); - FREE(mpp); + free(mpp->mpcontext); + free(mpp); } void @@ -633,7 +632,7 @@ int add_feature(char **f, const char *n) for (d = c; d >= 10; d /= 10) l++; - t = MALLOC(l + 1); + t = calloc(1, l + 1); if (!t) return 1; @@ -644,7 +643,7 @@ int add_feature(char **f, const char *n) snprintf(t, l + 1, "%0d%s %s", c, e, n); - FREE(*f); + free(*f); *f = t; return 0; @@ -696,7 +695,7 @@ int remove_feature(char **f, const char *o) /* Quick exit if all features have been removed */ if (c == 0) { - n = MALLOC(2); + n = malloc(2); if (!n) return 1; strcpy(n, "0"); @@ -711,7 +710,7 @@ int remove_feature(char **f, const char *o) /* Update feature count space */ l = strlen(*f) - d; - n = MALLOC(l + 1); + n = malloc(l + 1); if (!n) return 1; @@ -724,7 +723,7 @@ int remove_feature(char **f, const char *o) p = strchr(*f, ' '); if (!p) { /* Internal error, feature string inconsistent */ - FREE(n); + free(n); return 1; } while (*p == ' ') @@ -753,7 +752,7 @@ int remove_feature(char **f, const char *o) } out: - FREE(*f); + free(*f); *f = n; return 0; diff --git a/libmultipath/structs.h b/libmultipath/structs.h index 399540e..c0f8929 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -200,6 +200,13 @@ enum initialized_states { * mapped by some multipath map because of map reload failure. */ INIT_REMOVED, + /* + * INIT_PARTIAL: paths added by update_pathvec_from_dm() will not + * be fully initialized. This will be handled when an add or + * change uevent is received. + */ + INIT_PARTIAL, + __INIT_LAST, }; enum prkey_sources { @@ -312,6 +319,7 @@ struct path { int fd; int initialized; int retriggers; + int partial_retrigger_delay; unsigned int path_failures; time_t dis_reinstate_time; int disable_reinstate; @@ -355,6 +363,7 @@ struct multipath { int retain_hwhandler; int deferred_remove; bool in_recovery; + bool need_reload; int san_path_err_threshold; int san_path_err_forget_rate; int san_path_err_recovery_time; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 85d97ac..cb0ebae 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "util.h" #include "checkers.h" @@ -98,7 +99,7 @@ static bool guess_mpp_wwid(struct multipath *mpp) * - may set pp->wwid and / or mpp->wwid * - calls pathinfo() on existing paths is pathinfo_flags is not 0 */ -bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, +static bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, int pathinfo_flags) { int i, j; @@ -148,16 +149,15 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, * uninitialized struct path to pgp->paths, with only * pp->dev_t filled in. Thus if pp->udev is set here, * we know that the path is in pathvec already. - * However, it's possible that the path in pathvec is - * different from the one the kernel still had in its - * map. */ if (pp->udev) { if (pathinfo_flags & ~DI_NOIO) { conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - pathinfo(pp, conf, pathinfo_flags|DI_WWID); + if (pathinfo(pp, conf, pathinfo_flags) != PATHINFO_OK) + condlog(2, "%s: pathinfo failed for existing path %s (flags=0x%x)", + __func__, pp->dev, pathinfo_flags); pthread_cleanup_pop(1); } } else { @@ -174,15 +174,14 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, } else { int rc; - devt2devname(pp->dev, sizeof(pp->dev), - pp->dev_t); + strlcpy(pp->dev, udev_device_get_sysname(pp->udev), + sizeof(pp->dev)); conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); pp->checkint = conf->checkint; rc = pathinfo(pp, conf, - DI_SYSFS|DI_WWID|DI_BLACKLIST| - pathinfo_flags); + DI_SYSFS|DI_WWID|DI_BLACKLIST|DI_NOFALLBACK|pathinfo_flags); pthread_cleanup_pop(1); if (rc != PATHINFO_OK) { condlog(1, "%s: error %d in pathinfo, discarding path", @@ -194,6 +193,8 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, } condlog(2, "%s: adding new path %s", mpp->alias, pp->dev); + pp->initialized = INIT_PARTIAL; + pp->partial_retrigger_delay = 180; store_path(pathvec, pp); pp->tick = 1; } @@ -237,6 +238,7 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, free_pathgroup(pgp, KEEP_PATHS); must_reload = true; } + mpp->need_reload = mpp->need_reload || must_reload; return must_reload; } @@ -298,16 +300,19 @@ void orphan_path(struct path *pp, const char *reason) uninitialize_path(pp); } -void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason) +static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason) { int i; struct path * pp; vector_foreach_slot (pathvec, pp, i) { if (pp->mpp == mpp) { - if (pp->initialized == INIT_REMOVED) { - condlog(3, "%s: freeing path in removed state", - pp->dev); + if (pp->initialized == INIT_REMOVED || + pp->initialized == INIT_PARTIAL) { + condlog(3, "%s: freeing path in %s state", + pp->dev, + pp->initialized == INIT_REMOVED ? + "removed" : "partial"); vector_del_slot(pathvec, i--); free_path(pp); } else @@ -325,10 +330,8 @@ void set_path_removed(struct path *pp) * Keep link to mpp. It will be removed when the path * is successfully removed from the map. */ - if (!mpp) { + if (!mpp) condlog(0, "%s: internal error: mpp == NULL", pp->dev); - return; - } pp->mpp = mpp; pp->initialized = INIT_REMOVED; } @@ -338,6 +341,10 @@ remove_map(struct multipath *mpp, vector pathvec, vector mpvec) { int i; + free_pathvec(mpp->paths, KEEP_PATHS); + free_pgvec(mpp->pg, KEEP_PATHS); + mpp->paths = mpp->pg = NULL; + /* * clear references to this map */ @@ -393,12 +400,12 @@ extract_hwe_from_path(struct multipath * mpp) condlog(4, "%s: searching paths for valid hwe", mpp->alias); /* doing this in two passes seems like paranoia to me */ vector_foreach_slot(mpp->paths, pp, i) { - if (pp->state == PATH_UP && + if (pp->state == PATH_UP && pp->initialized != INIT_PARTIAL && pp->initialized != INIT_REMOVED && pp->hwe) goto done; } vector_foreach_slot(mpp->paths, pp, i) { - if (pp->state != PATH_UP && + if ((pp->state != PATH_UP || pp->initialized == INIT_PARTIAL) && pp->initialized != INIT_REMOVED && pp->hwe) goto done; } @@ -468,11 +475,14 @@ static void check_removed_paths(const struct multipath *mpp, vector pathvec) int i; vector_foreach_slot(pathvec, pp, i) { - if (pp->initialized != INIT_REMOVED || pp->mpp != mpp) - continue; - if (!find_devt_in_pathgroups(mpp, pp->dev_t)) { - condlog(2, "%s: %s: freeing path in removed state", - __func__, pp->dev); + if (pp->mpp == mpp && + (pp->initialized == INIT_REMOVED || + pp->initialized == INIT_PARTIAL) && + !find_devt_in_pathgroups(mpp, pp->dev_t)) { + condlog(2, "%s: %s: freeing path in %s state", + __func__, pp->dev, + pp->initialized == INIT_REMOVED ? + "removed" : "partial"); vector_del_slot(pathvec, i--); free_path(pp); } diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h index 29ede45..d33fe98 100644 --- a/libmultipath/structs_vec.h +++ b/libmultipath/structs_vec.h @@ -6,23 +6,19 @@ #include "lock.h" struct vectors { - struct mutex_lock lock; /* defined in lock.h */ vector pathvec; vector mpvec; + struct mutex_lock lock; /* defined in lock.h */ }; void __set_no_path_retry(struct multipath *mpp, bool check_features); #define set_no_path_retry(mpp) __set_no_path_retry(mpp, true) int adopt_paths (vector pathvec, struct multipath * mpp); -void orphan_paths(vector pathvec, struct multipath *mpp, - const char *reason); void orphan_path (struct path * pp, const char *reason); void set_path_removed(struct path *pp); int verify_paths(struct multipath *mpp); -bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp, - int pathinfo_flags); int update_mpp_paths(struct multipath * mpp, vector pathvec); int update_multipath_strings (struct multipath *mpp, vector pathvec); void extract_hwe_from_path(struct multipath * mpp); diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c index 9ff145f..f45dbee 100644 --- a/libmultipath/sysfs.c +++ b/libmultipath/sysfs.c @@ -287,7 +287,7 @@ int sysfs_check_holders(char * check_devt, char * new_devt) table_name, check_devt, new_devt); dm_reassign_table(table_name, check_devt, new_devt); - FREE(table_name); + free(table_name); } closedir(dirfd); diff --git a/libmultipath/time-util.c b/libmultipath/time-util.c index 55f366c..2919300 100644 --- a/libmultipath/time-util.c +++ b/libmultipath/time-util.c @@ -49,3 +49,15 @@ void timespecsub(const struct timespec *a, const struct timespec *b, res->tv_nsec = a->tv_nsec - b->tv_nsec; normalize_timespec(res); } + +int timespeccmp(const struct timespec *a, const struct timespec *b) +{ + struct timespec tmp; + + timespecsub(a, b, &tmp); + if (tmp.tv_sec > 0) + return 1; + if (tmp.tv_sec < 0) + return -1; + return tmp.tv_nsec > 0 ? 1 : (tmp.tv_nsec < 0 ? -1 : 0); +} diff --git a/libmultipath/time-util.h b/libmultipath/time-util.h index b23d328..4a80ebd 100644 --- a/libmultipath/time-util.h +++ b/libmultipath/time-util.h @@ -10,5 +10,6 @@ void pthread_cond_init_mono(pthread_cond_t *cond); void normalize_timespec(struct timespec *ts); void timespecsub(const struct timespec *a, const struct timespec *b, struct timespec *res); +int timespeccmp(const struct timespec *a, const struct timespec *b); #endif /* _TIME_UTIL_H_ */ diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 4265904..70ad217 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -43,7 +43,6 @@ #include #include -#include "memory.h" #include "debug.h" #include "list.h" #include "uevent.h" @@ -81,7 +80,7 @@ int is_uevent_busy(void) struct uevent * alloc_uevent (void) { - struct uevent *uev = MALLOC(sizeof(struct uevent)); + struct uevent *uev = calloc(1, sizeof(struct uevent)); if (uev) { INIT_LIST_HEAD(&uev->node); @@ -91,16 +90,25 @@ struct uevent * alloc_uevent (void) return uev; } +static void uevq_cleanup(struct list_head *tmpq); + +static void cleanup_uev(void *arg) +{ + struct uevent *uev = arg; + + uevq_cleanup(&uev->merge_node); + if (uev->udev) + udev_device_unref(uev->udev); + free(uev); +} + static void uevq_cleanup(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); - - if (uev->udev) - udev_device_unref(uev->udev); - FREE(uev); + cleanup_uev(uev); } } @@ -309,7 +317,7 @@ uevent_prepare(struct list_head *tmpq) list_del_init(&uev->node); if (uev->udev) udev_device_unref(uev->udev); - FREE(uev); + free(uev); continue; } @@ -337,7 +345,7 @@ uevent_filter(struct uevent *later, struct list_head *tmpq) list_del_init(&earlier->node); if (earlier->udev) udev_device_unref(earlier->udev); - FREE(earlier); + free(earlier); } } } @@ -384,14 +392,10 @@ service_uevq(struct list_head *tmpq) list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); + pthread_cleanup_push(cleanup_uev, uev); if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data)) condlog(0, "uevent trigger error"); - - uevq_cleanup(&uev->merge_node); - - if (uev->udev) - udev_device_unref(uev->udev); - FREE(uev); + pthread_cleanup_pop(1); } } @@ -411,6 +415,18 @@ static void monitor_cleanup(void *arg) udev_monitor_unref(monitor); } +static void cleanup_uevq(void *arg) +{ + uevq_cleanup(arg); +} + +static void cleanup_global_uevq(void *arg __attribute__((unused))) +{ + pthread_mutex_lock(uevq_lockp); + uevq_cleanup(&uevq); + pthread_mutex_unlock(uevq_lockp); +} + /* * Service the uevent queue. */ @@ -425,6 +441,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data), while (1) { LIST_HEAD(uevq_tmp); + pthread_cleanup_push(cleanup_mutex, uevq_lockp); pthread_mutex_lock(uevq_lockp); servicing_uev = 0; /* @@ -436,14 +453,17 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data), } servicing_uev = 1; list_splice_init(&uevq, &uevq_tmp); - pthread_mutex_unlock(uevq_lockp); + pthread_cleanup_pop(1); + if (!my_uev_trigger) break; + + pthread_cleanup_push(cleanup_uevq, &uevq_tmp); merge_uevq(&uevq_tmp); service_uevq(&uevq_tmp); + pthread_cleanup_pop(1); } condlog(3, "Terminating uev service queue"); - uevq_cleanup(&uevq); return 0; } @@ -492,7 +512,7 @@ static struct uevent *uevent_from_udev_device(struct udev_device *dev) if (!uev->devpath || ! uev->action) { udev_device_unref(dev); condlog(1, "uevent missing necessary fields"); - FREE(uev); + free(uev); return NULL; } uev->udev = dev; @@ -600,6 +620,8 @@ int uevent_listen(struct udev *udev) events = 0; gettimeofday(&start_time, NULL); + pthread_cleanup_push(cleanup_global_uevq, NULL); + pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp); while (1) { struct uevent *uev; struct udev_device *dev; @@ -650,6 +672,8 @@ int uevent_listen(struct udev *udev) gettimeofday(&start_time, NULL); timeout = 30; } + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); out: pthread_cleanup_pop(1); out_udev: diff --git a/libmultipath/util.c b/libmultipath/util.c index ea85840..ce5ea73 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -17,7 +17,6 @@ #include "util.h" #include "debug.h" -#include "memory.h" #include "checkers.h" #include "vector.h" #include "structs.h" @@ -110,7 +109,7 @@ get_word (const char *sentence, char **word) if (!word) return skip + len; - *word = MALLOC(len + 1); + *word = calloc(1, len + 1); if (!*word) { condlog(0, "get_word : oom"); @@ -168,6 +167,7 @@ size_t strlcat(char * restrict dst, const char * restrict src, size_t size) int devt2devname(char *devname, int devname_len, const char *devt) { struct udev_device *u_dev; + const char * dev_name; int r; if (!devname || !devname_len || !devt) @@ -178,7 +178,13 @@ int devt2devname(char *devname, int devname_len, const char *devt) condlog(0, "\"%s\": invalid major/minor numbers, not found in sysfs", devt); return 1; } - r = strlcpy(devname, udev_device_get_sysname(u_dev), devname_len); + + dev_name = udev_device_get_sysname(u_dev); + if (!dev_name) { + udev_device_unref(u_dev); + return 1; + } + r = strlcpy(devname, dev_name, devname_len); udev_device_unref(u_dev); return !(r < devname_len); @@ -407,8 +413,8 @@ void free_scandir_result(struct scandir_result *res) int i; for (i = 0; i < res->n; i++) - FREE(res->di[i]); - FREE(res->di); + free(res->di[i]); + free(res->di); } void close_fd(void *arg) @@ -460,3 +466,8 @@ void cleanup_charp(char **p) { free(*p); } + +void cleanup_ucharp(unsigned char **p) +{ + free(*p); +} diff --git a/libmultipath/util.h b/libmultipath/util.h index 89027f8..79d9f32 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -9,6 +9,7 @@ #include #include #include +#include size_t strchop(char *); int basenamecpy (const char *src, char *dst, size_t size); @@ -124,4 +125,5 @@ static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf) }) void cleanup_charp(char **p); +void cleanup_ucharp(unsigned char **p); #endif /* _UTIL_H */ diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c index 6adeedf..2135476 100644 --- a/libmultipath/uxsock.c +++ b/libmultipath/uxsock.c @@ -21,7 +21,6 @@ #endif #include "mpath_cmd.h" -#include "memory.h" #include "uxsock.h" #include "debug.h" @@ -110,12 +109,12 @@ static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit) return -errno; if ((limit > 0) && (len > limit)) return -EINVAL; - (*buf) = MALLOC(len); + (*buf) = calloc(1, len); if (!*buf) return -ENOMEM; err = mpath_recv_reply_data(fd, *buf, len, timeout); if (err != 0) { - FREE(*buf); + free(*buf); (*buf) = NULL; return -errno; } @@ -129,8 +128,3 @@ int recv_packet(int fd, char **buf, unsigned int timeout) { return _recv_packet(fd, buf, timeout, 0 /* no limit */); } - -int recv_packet_from_client(int fd, char **buf, unsigned int timeout) -{ - return _recv_packet(fd, buf, timeout, _MAX_CMD_LEN); -} diff --git a/libmultipath/uxsock.h b/libmultipath/uxsock.h index 8e7401d..e3d28cf 100644 --- a/libmultipath/uxsock.h +++ b/libmultipath/uxsock.h @@ -5,9 +5,3 @@ int recv_packet(int fd, char **buf, unsigned int timeout); #define _MAX_CMD_LEN 512 -/* - * Used for receiving socket command from untrusted socket client where data - * size is restricted to 512(_MAX_CMD_LEN) at most. - * Return -EINVAL if data length requested by client exceeded the _MAX_CMD_LEN. - */ -int recv_packet_from_client(int fd, char **buf, unsigned int timeout); diff --git a/libmultipath/vector.c b/libmultipath/vector.c index 6605eb2..e2d1ec9 100644 --- a/libmultipath/vector.c +++ b/libmultipath/vector.c @@ -19,7 +19,6 @@ * Copyright (c) 2005 Christophe Varoqui */ -#include "memory.h" #include #include "vector.h" @@ -30,7 +29,7 @@ vector vector_alloc(void) { - vector v = (vector) MALLOC(sizeof (struct _vector)); + vector v = (vector) calloc(1, sizeof (struct _vector)); return v; } @@ -46,7 +45,7 @@ vector_alloc_slot(vector v) return false; new_allocated = v->allocated + VECTOR_DEFAULT_SIZE; - new_slot = REALLOC(v->slot, sizeof (void *) * new_allocated); + new_slot = realloc(v->slot, sizeof (void *) * new_allocated); if (!new_slot) return false; @@ -119,13 +118,13 @@ vector_del_slot(vector v, int slot) v->allocated -= VECTOR_DEFAULT_SIZE; if (v->allocated <= 0) { - FREE(v->slot); + free(v->slot); v->slot = NULL; v->allocated = 0; } else { void *new_slot; - new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated); + new_slot = realloc(v->slot, sizeof (void *) * v->allocated); if (!new_slot) v->allocated += VECTOR_DEFAULT_SIZE; else @@ -153,7 +152,7 @@ vector_reset(vector v) return NULL; if (v->slot) - FREE(v->slot); + free(v->slot); v->allocated = 0; v->slot = NULL; @@ -166,7 +165,7 @@ vector_free(vector v) { if (!vector_reset(v)) return; - FREE(v); + free(v); } void @@ -180,7 +179,7 @@ free_strvec(vector strvec) vector_foreach_slot (strvec, str, i) if (str) - FREE(str); + free(str); vector_free(strvec); } diff --git a/libmultipath/version.h b/libmultipath/version.h index 34e1d90..e86a3cc 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000807 -#define DATE_CODE 0x090815 +#define VERSION_CODE 0x000808 +#define DATE_CODE 0x030c15 #define PROG "multipath-tools" diff --git a/mpathpersist/mpathpersist.8 b/mpathpersist/mpathpersist.8 index 882043a..7b57459 100644 --- a/mpathpersist/mpathpersist.8 +++ b/mpathpersist/mpathpersist.8 @@ -235,7 +235,7 @@ multiple lines is not supported. .PP All options listed in this man page, except \fI-f\fR and \fI-v\fR, are allowed in batch files. Both short and long option formats may be used. -Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR +Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR option is ignored in batch files. . .PP @@ -253,7 +253,7 @@ of the first failed command, or 0 if all commands succeeded. .PP If other options and parameters are used along with \fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed -by the commands from the the batch file. +by the commands from the batch file. . .PP Below is an example of a valid batch input file. diff --git a/multipath/main.c b/multipath/main.c index 65ece83..0a9377e 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -35,7 +35,6 @@ #include "checkers.h" #include "prio.h" #include "vector.h" -#include "memory.h" #include #include "devmapper.h" #include "util.h" @@ -82,7 +81,7 @@ dump_config (struct config *conf, vector hwes, vector mpvec) if (reply != NULL) { printf("%s", reply); - FREE(reply); + free(reply); return 0; } else return 1; @@ -286,7 +285,7 @@ found: condlog(r == 0 ? 3 : 2, "%s:%s usable paths found", devpath, r == 0 ? "" : " no"); free: - FREE(mapname); + free(mapname); free_multipath(mpp, FREE_PATHS); vector_free(pathvec); out: @@ -461,6 +460,7 @@ configure (struct config *conf, enum mpath_cmds cmd, int di_flag = 0; char * refwwid = NULL; char * dev = NULL; + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; /* * allocate core vectors to store paths and multipaths @@ -547,8 +547,10 @@ configure (struct config *conf, enum mpath_cmds cmd, if (libmp_verbosity > 2) print_all_paths(pathvec, 1); - get_path_layout(pathvec, 0); - foreign_path_layout(); + if ((width = alloc_path_layout()) == NULL) + goto out; + get_path_layout(pathvec, 0, width); + foreign_path_layout(width); if (get_dm_mpvec(cmd, curmp, pathvec, refwwid)) goto out; @@ -560,6 +562,7 @@ configure (struct config *conf, enum mpath_cmds cmd, dump_config(conf, hwes, curmp); vector_free(hwes); + r = RTVL_OK; goto out; } @@ -584,7 +587,7 @@ out: condlog(2, "Warning: multipath devices exist, but multipathd service is not running"); if (refwwid) - FREE(refwwid); + free(refwwid); free_multipathvec(curmp, KEEP_PATHS); vecs.mpvec = NULL; @@ -759,7 +762,7 @@ int delegate_to_multipathd(enum mpath_cmds cmd, return NOT_DELEGATED; if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { - p += snprintf(p, n, "reconfigure"); + p += snprintf(p, n, "reconfigure all"); } else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) { p += snprintf(p, n, "del map %s", dev); @@ -807,7 +810,7 @@ int delegate_to_multipathd(enum mpath_cmds cmd, } out: - FREE(reply); + free(reply); close(fd); return r; } @@ -837,14 +840,12 @@ main (int argc, char *argv[]) conf = get_multipath_config(); conf->retrigger_tries = 0; conf->force_sync = 1; - atexit(cleanup_vecs); + if (atexit(cleanup_vecs)) + condlog(1, "failed to register cleanup handler for vecs: %m"); while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) { switch(arg) { - case 1: printf("optarg : %s\n",optarg); - break; case 'v': - if (sizeof(optarg) > sizeof(char *) || - !isdigit(optarg[0])) { + if (!isdigit(optarg[0])) { usage (argv[0]); exit(RTVL_FAIL); } @@ -887,9 +888,6 @@ main (int argc, char *argv[]) break; case 'M': -#if _DEBUG_ - debug = atoi(optarg); -#endif break; case 'p': conf->pgpolicy_flag = get_pgpolicy_id(optarg); @@ -960,10 +958,13 @@ main (int argc, char *argv[]) exit(RTVL_FAIL); } - check_alias_settings(conf); + if (check_alias_settings(conf)) { + fprintf(stderr, "fatal configuration error, aborting"); + exit(RTVL_FAIL); + } if (optind < argc) { - dev = MALLOC(FILE_NAME_SIZE); + dev = calloc(1, FILE_NAME_SIZE); if (!dev) goto out; @@ -1074,13 +1075,10 @@ main (int argc, char *argv[]) out: put_multipath_config(conf); if (dev) - FREE(dev); + free(dev); if (dev_type == DEV_UEVENT) closelog(); -#ifdef _DEBUG_ - dbg_free_final(NULL); -#endif return r; } diff --git a/multipath/multipath.8 b/multipath/multipath.8 index 17df59f..4c7e988 100644 --- a/multipath/multipath.8 +++ b/multipath/multipath.8 @@ -245,7 +245,7 @@ Treat the bindings file as read only. . .TP .BI \-b " file" -Set \fIuser_friendly_names\fR bindings file location. The default is +Set \fIuser_friendly_names\fR bindings file location. The default is \fI/etc/multipath/bindings\fR. . .TP diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index d6b8c7f..88d2a1d 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -1,12 +1,12 @@ .\" ---------------------------------------------------------------------------- -.\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t multipath/multipath.conf.5 .\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null .\" +.\" Update the date below if you make any significant change. .\" ---------------------------------------------------------------------------- . -.TH MULTIPATH.CONF 5 2018-05-21 Linux +.TH MULTIPATH.CONF 5 2021-09-08 Linux . . .\" ---------------------------------------------------------------------------- @@ -178,6 +178,7 @@ The default is: \fBno\fR . .TP .B multipath_dir +This option is deprecated, and will be removed in a future release. Directory where the dynamic shared objects are stored. Defined at compile time, commonly \fI/lib64/multipath/\fR or \fI/lib/multipath/\fR. .RS @@ -189,7 +190,7 @@ The default is: \fB\fR .TP .B path_selector The default path selector algorithm to use; they are offered by the -kernel multipath target. There are three selector algorithms: +kernel multipath target: .RS .TP 12 .I "round-robin 0" @@ -206,10 +207,14 @@ of outstanding I/O to the path. of outstanding I/O to the path and its relative throughput. .TP .I "historical-service-time 0" -(Since 5.8 kernel) Choose the path for the next bunch of IOs based on the +(Since 5.8 kernel) Choose the path for the next bunch of I/O based on the estimation of future service time based on the history of previous I/O submitted to each path. .TP +.I "io-affinity 0" +(Since 5.11 kernel) Choose the path for the next bunch of I/O based on a CPU to +path mapping the user passes in and what CPU we are executing on. +.TP The default is: \fBservice-time 0\fR .RE . @@ -738,6 +743,7 @@ The default is: \fB\fR . .TP .B bindings_file +This option is deprecated, and will be removed in a future release. The full pathname of the binding file to be used when the user_friendly_names option is set. .RS @@ -748,6 +754,7 @@ The default is: \fB/etc/multipath/bindings\fR . .TP .B wwids_file +This option is deprecated, and will be removed in a future release. The full pathname of the WWIDs file, which is used by multipath to keep track of the WWIDs for LUNs it has created multipath devices on in the past. .RS @@ -758,6 +765,7 @@ The default is: \fB/etc/multipath/wwids\fR . .TP .B prkeys_file +This option is deprecated, and will be removed in a future release. The full pathname of the prkeys file, which is used by multipathd to keep track of the persistent reservation key used for a specific WWID, when \fIreservation_key\fR is set to \fBfile\fR. @@ -929,6 +937,7 @@ The default is: \fB\fR . .TP .B config_dir +This option is deprecated, and will be removed in a future release. If set to anything other than "", multipath will search this directory alphabetically for file ending in ".conf" and it will read configuration information from them, just as if it was in \fI/etc/multipath.conf\fR. diff --git a/multipathd/cli.c b/multipathd/cli.c index 4d6c37c..b2ee9a9 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -4,7 +4,7 @@ #include #include #include -#include "memory.h" +#include #include "vector.h" #include "structs.h" #include "structs_vec.h" @@ -24,13 +24,13 @@ static vector handlers; static struct key * alloc_key (void) { - return (struct key *)MALLOC(sizeof(struct key)); + return (struct key *)calloc(1, sizeof(struct key)); } static struct handler * alloc_handler (void) { - return (struct handler *)MALLOC(sizeof(struct handler)); + return (struct handler *)calloc(1, sizeof(struct handler)); } static int @@ -45,7 +45,7 @@ add_key (vector vec, char * str, uint64_t code, int has_param) kw->code = code; kw->has_param = has_param; - kw->str = STRDUP(str); + kw->str = strdup(str); if (!kw->str) goto out; @@ -58,32 +58,32 @@ add_key (vector vec, char * str, uint64_t code, int has_param) return 0; out1: - FREE(kw->str); + free(kw->str); out: - FREE(kw); + free(kw); return 1; } -int -add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)) +static struct handler *add_handler(uint64_t fp, cli_handler *fn, bool locked) { struct handler * h; h = alloc_handler(); - if (!h) - return 1; + if (h == NULL) + return NULL; if (!vector_alloc_slot(handlers)) { - FREE(h); - return 1; + free(h); + return NULL; } vector_set_slot(handlers, h); h->fingerprint = fp; h->fn = fn; + h->locked = locked; - return 0; + return h; } static struct handler * @@ -100,26 +100,17 @@ find_handler (uint64_t fp) } int -set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)) -{ - struct handler * h = find_handler(fp); - - if (!h) - return 1; - h->fn = fn; - h->locked = 1; - return 0; -} - -int -set_unlocked_handler_callback (uint64_t fp,int (*fn)(void *, char **, int *, void *)) +__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked) { - struct handler * h = find_handler(fp); + struct handler *h; - if (!h) + assert(find_handler(fp) == NULL); + h = add_handler(fp, fn, locked); + if (!h) { + condlog(0, "%s: failed to set handler for code %"PRIu64, + __func__, fp); return 1; - h->fn = fn; - h->locked = 0; + } return 0; } @@ -127,12 +118,12 @@ static void free_key (struct key * kw) { if (kw->str) - FREE(kw->str); + free(kw->str); if (kw->param) - FREE(kw->param); + free(kw->param); - FREE(kw); + free(kw); } void @@ -154,7 +145,7 @@ free_handlers (void) struct handler * h; vector_foreach_slot (handlers, h, i) - FREE(h); + free(h); vector_free(handlers); handlers = NULL; @@ -218,6 +209,7 @@ load_keys (void) r += add_key(keys, "local", LOCAL, 0); r += add_key(keys, "setmarginal", SETMARGINAL, 0); r += add_key(keys, "unsetmarginal", UNSETMARGINAL, 0); + r += add_key(keys, "all", ALL, 0); if (r) { @@ -259,11 +251,10 @@ find_key (const char * str) * * returns: * ENOMEM: not enough memory to allocate command - * EAGAIN: command not found + * ESRCH: command not found * EINVAL: argument missing for command */ -static int -get_cmdvec (char * cmd, vector *v) +int get_cmdvec (char *cmd, vector *v) { int i; int r = 0; @@ -294,7 +285,7 @@ get_cmdvec (char * cmd, vector *v) } kw = find_key(buff); if (!kw) { - r = EAGAIN; + r = ESRCH; goto out; } cmdkw = alloc_key(); @@ -303,7 +294,7 @@ get_cmdvec (char * cmd, vector *v) goto out; } if (!vector_alloc_slot(cmdvec)) { - FREE(cmdkw); + free(cmdkw); r = ENOMEM; goto out; } @@ -328,7 +319,7 @@ out: } static uint64_t -fingerprint(vector vec) +fingerprint(const struct _vector *vec) { int i; uint64_t fp = 0; @@ -343,6 +334,11 @@ fingerprint(vector vec) return fp; } +struct handler *find_handler_for_cmdvec(const struct _vector *v) +{ + return find_handler(fingerprint(v)); +} + int alloc_handlers (void) { @@ -384,7 +380,7 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) { case ENOMEM: rc = print_strbuf(reply, "%s: Not enough memory\n", cmd); break; - case EAGAIN: + case ESRCH: rc = print_strbuf(reply, "%s: not found\n", cmd); break; case EINVAL: @@ -421,75 +417,10 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) { } -static char * -genhelp_handler (const char *cmd, int error) +void genhelp_handler(const char *cmd, int error, struct strbuf *reply) { - STRBUF_ON_STACK(reply); - - if (do_genhelp(&reply, cmd, error) == -1) + if (do_genhelp(reply, cmd, error) == -1) condlog(0, "genhelp_handler: out of memory"); - return steal_strbuf_str(&reply); -} - -int -parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) -{ - int r; - struct handler * h; - vector cmdvec = NULL; - struct timespec tmo; - - r = get_cmdvec(cmd, &cmdvec); - - if (r) { - *reply = genhelp_handler(cmd, r); - if (*reply == NULL) - return EINVAL; - *len = strlen(*reply) + 1; - return 0; - } - - h = find_handler(fingerprint(cmdvec)); - - if (!h || !h->fn) { - free_keys(cmdvec); - *reply = genhelp_handler(cmd, EINVAL); - if (*reply == NULL) - return EINVAL; - *len = strlen(*reply) + 1; - return 0; - } - - /* - * execute handler - */ - if (clock_gettime(CLOCK_REALTIME, &tmo) == 0) { - tmo.tv_sec += timeout; - } else { - tmo.tv_sec = 0; - } - if (h->locked) { - int locked = 0; - struct vectors * vecs = (struct vectors *)data; - - pthread_cleanup_push(cleanup_lock, &vecs->lock); - if (tmo.tv_sec) { - r = timedlock(&vecs->lock, &tmo); - } else { - lock(&vecs->lock); - r = 0; - } - if (r == 0) { - locked = 1; - pthread_testcancel(); - r = h->fn(cmdvec, reply, len, data); - } - pthread_cleanup_pop(locked); - } else - r = h->fn(cmdvec, reply, len, data); - free_keys(cmdvec); - - return r; } char * @@ -513,63 +444,6 @@ cli_init (void) { if (alloc_handlers()) return 1; - add_handler(LIST+PATHS, NULL); - add_handler(LIST+PATHS+FMT, NULL); - add_handler(LIST+PATHS+RAW+FMT, NULL); - add_handler(LIST+PATH, NULL); - add_handler(LIST+STATUS, NULL); - add_handler(LIST+DAEMON, NULL); - add_handler(LIST+MAPS, NULL); - add_handler(LIST+MAPS+STATUS, NULL); - add_handler(LIST+MAPS+STATS, NULL); - add_handler(LIST+MAPS+FMT, NULL); - add_handler(LIST+MAPS+RAW+FMT, NULL); - add_handler(LIST+MAPS+TOPOLOGY, NULL); - add_handler(LIST+MAPS+JSON, NULL); - add_handler(LIST+TOPOLOGY, NULL); - add_handler(LIST+MAP+TOPOLOGY, NULL); - add_handler(LIST+MAP+JSON, NULL); - add_handler(LIST+MAP+FMT, NULL); - add_handler(LIST+MAP+RAW+FMT, NULL); - add_handler(LIST+CONFIG, NULL); - add_handler(LIST+CONFIG+LOCAL, NULL); - add_handler(LIST+BLACKLIST, NULL); - add_handler(LIST+DEVICES, NULL); - add_handler(LIST+WILDCARDS, NULL); - add_handler(RESET+MAPS+STATS, NULL); - add_handler(RESET+MAP+STATS, NULL); - add_handler(ADD+PATH, NULL); - add_handler(DEL+PATH, NULL); - add_handler(ADD+MAP, NULL); - add_handler(DEL+MAP, NULL); - add_handler(DEL+MAPS, NULL); - add_handler(SWITCH+MAP+GROUP, NULL); - add_handler(RECONFIGURE, NULL); - add_handler(SUSPEND+MAP, NULL); - add_handler(RESUME+MAP, NULL); - add_handler(RESIZE+MAP, NULL); - add_handler(RESET+MAP, NULL); - add_handler(RELOAD+MAP, NULL); - add_handler(DISABLEQ+MAP, NULL); - add_handler(RESTOREQ+MAP, NULL); - add_handler(DISABLEQ+MAPS, NULL); - add_handler(RESTOREQ+MAPS, NULL); - add_handler(REINSTATE+PATH, NULL); - add_handler(FAIL+PATH, NULL); - add_handler(QUIT, NULL); - add_handler(SHUTDOWN, NULL); - add_handler(GETPRSTATUS+MAP, NULL); - add_handler(SETPRSTATUS+MAP, NULL); - add_handler(UNSETPRSTATUS+MAP, NULL); - add_handler(GETPRKEY+MAP, NULL); - add_handler(SETPRKEY+MAP+KEY, NULL); - add_handler(UNSETPRKEY+MAP, NULL); - add_handler(FORCEQ+DAEMON, NULL); - add_handler(RESTOREQ+DAEMON, NULL); - add_handler(SETMARGINAL+PATH, NULL); - add_handler(UNSETMARGINAL+PATH, NULL); - add_handler(UNSETMARGINAL+MAP, NULL); - return 0; } diff --git a/multipathd/cli.h b/multipathd/cli.h index fdfb9ae..a6082ac 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -4,83 +4,84 @@ #include enum { - __LIST, + __LIST, /* 0 */ __ADD, __DEL, __SWITCH, __SUSPEND, - __RESUME, + __RESUME, /* 5 */ __REINSTATE, __FAIL, __RESIZE, __RESET, - __RELOAD, + __RELOAD, /* 10 */ __FORCEQ, __DISABLEQ, __RESTOREQ, __PATHS, - __MAPS, + __MAPS, /* 15 */ __PATH, __MAP, __GROUP, __RECONFIGURE, - __DAEMON, + __DAEMON, /* 20 */ __STATUS, __STATS, __TOPOLOGY, __CONFIG, - __BLACKLIST, + __BLACKLIST, /* 25 */ __DEVICES, __RAW, __WILDCARDS, __QUIT, - __SHUTDOWN, + __SHUTDOWN, /* 30 */ __GETPRSTATUS, __SETPRSTATUS, __UNSETPRSTATUS, __FMT, - __JSON, + __JSON, /* 35 */ __GETPRKEY, __SETPRKEY, __UNSETPRKEY, __KEY, - __LOCAL, + __LOCAL, /* 40 */ __SETMARGINAL, __UNSETMARGINAL, + __ALL, }; -#define LIST (1 << __LIST) -#define ADD (1 << __ADD) -#define DEL (1 << __DEL) -#define SWITCH (1 << __SWITCH) -#define SUSPEND (1 << __SUSPEND) -#define RESUME (1 << __RESUME) -#define REINSTATE (1 << __REINSTATE) -#define FAIL (1 << __FAIL) -#define RESIZE (1 << __RESIZE) -#define RESET (1 << __RESET) -#define RELOAD (1 << __RELOAD) -#define FORCEQ (1 << __FORCEQ) -#define DISABLEQ (1 << __DISABLEQ) -#define RESTOREQ (1 << __RESTOREQ) -#define PATHS (1 << __PATHS) -#define MAPS (1 << __MAPS) -#define PATH (1 << __PATH) -#define MAP (1 << __MAP) -#define GROUP (1 << __GROUP) -#define RECONFIGURE (1 << __RECONFIGURE) -#define DAEMON (1 << __DAEMON) -#define STATUS (1 << __STATUS) -#define STATS (1 << __STATS) -#define TOPOLOGY (1 << __TOPOLOGY) -#define CONFIG (1 << __CONFIG) -#define BLACKLIST (1 << __BLACKLIST) -#define DEVICES (1 << __DEVICES) -#define RAW (1 << __RAW) -#define COUNT (1 << __COUNT) -#define WILDCARDS (1 << __WILDCARDS) -#define QUIT (1 << __QUIT) -#define SHUTDOWN (1 << __SHUTDOWN) +#define LIST (1ULL << __LIST) +#define ADD (1ULL << __ADD) +#define DEL (1ULL << __DEL) +#define SWITCH (1ULL << __SWITCH) +#define SUSPEND (1ULL << __SUSPEND) +#define RESUME (1ULL << __RESUME) +#define REINSTATE (1ULL << __REINSTATE) +#define FAIL (1ULL << __FAIL) +#define RESIZE (1ULL << __RESIZE) +#define RESET (1ULL << __RESET) +#define RELOAD (1ULL << __RELOAD) +#define FORCEQ (1ULL << __FORCEQ) +#define DISABLEQ (1ULL << __DISABLEQ) +#define RESTOREQ (1ULL << __RESTOREQ) +#define PATHS (1ULL << __PATHS) +#define MAPS (1ULL << __MAPS) +#define PATH (1ULL << __PATH) +#define MAP (1ULL << __MAP) +#define GROUP (1ULL << __GROUP) +#define RECONFIGURE (1ULL << __RECONFIGURE) +#define DAEMON (1ULL << __DAEMON) +#define STATUS (1ULL << __STATUS) +#define STATS (1ULL << __STATS) +#define TOPOLOGY (1ULL << __TOPOLOGY) +#define CONFIG (1ULL << __CONFIG) +#define BLACKLIST (1ULL << __BLACKLIST) +#define DEVICES (1ULL << __DEVICES) +#define RAW (1ULL << __RAW) +#define COUNT (1ULL << __COUNT) +#define WILDCARDS (1ULL << __WILDCARDS) +#define QUIT (1ULL << __QUIT) +#define SHUTDOWN (1ULL << __SHUTDOWN) #define GETPRSTATUS (1ULL << __GETPRSTATUS) #define SETPRSTATUS (1ULL << __SETPRSTATUS) #define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS) @@ -93,6 +94,7 @@ enum { #define LOCAL (1ULL << __LOCAL) #define SETMARGINAL (1ULL << __SETMARGINAL) #define UNSETMARGINAL (1ULL << __UNSETMARGINAL) +#define ALL (1ULL << __ALL) #define INITIAL_REPLY_LEN 1200 @@ -106,7 +108,7 @@ enum { free(tmp); \ (r) = NULL; \ } else { \ - (r) = REALLOC((r), (m) * 2); \ + (r) = realloc((r), (m) * 2); \ if ((r)) { \ memset((r) + (m), 0, (m)); \ (m) *= 2; \ @@ -124,17 +126,25 @@ struct key { int has_param; }; +struct strbuf; + +typedef int (cli_handler)(void *keywords, struct strbuf *reply, void *data); + struct handler { uint64_t fingerprint; int locked; - int (*fn)(void *, char **, int *, void *); + cli_handler *fn; }; int alloc_handlers (void); -int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)); -int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); -int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); -int parse_cmd (char * cmd, char ** reply, int * len, void *, int); +int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked); +#define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true) +#define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false) + +int get_cmdvec (char *cmd, vector *v); +struct handler *find_handler_for_cmdvec(const struct _vector *v); +void genhelp_handler (const char *cmd, int error, struct strbuf *reply); + int load_keys (void); char * get_keyparam (vector v, uint64_t code); void free_keys (vector vec); diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 6d3a0ae..d79cdd7 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -5,7 +5,6 @@ #define _GNU_SOURCE #include "checkers.h" -#include "memory.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" @@ -33,105 +32,93 @@ #include "strbuf.h" #include "cli_handlers.h" -#define SET_REPLY_AND_LEN(__rep, __len, string_literal) \ - do { \ - *(__rep) = strdup(string_literal); \ - *(__len) = *(__rep) ? sizeof(string_literal) : 0; \ - } while (0) - -int -show_paths (char ** r, int * len, struct vectors * vecs, char * style, - int pretty) +static int +show_paths (struct strbuf *reply, struct vectors *vecs, char *style, int pretty) { - STRBUF_ON_STACK(reply); int i; struct path * pp; int hdr_len = 0; + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; - get_path_layout(vecs->pathvec, 1); - foreign_path_layout(); - - if (pretty && (hdr_len = snprint_path_header(&reply, style)) < 0) + if (pretty) { + if ((width = alloc_path_layout()) == NULL) + return 1; + get_path_layout(vecs->pathvec, 1, width); + foreign_path_layout(width); + } + if (pretty && (hdr_len = snprint_path_header(reply, style, width)) < 0) return 1; vector_foreach_slot(vecs->pathvec, pp, i) { - if (snprint_path(&reply, style, pp, pretty) < 0) + if (snprint_path(reply, style, pp, width) < 0) return 1; } - if (snprint_foreign_paths(&reply, style, pretty) < 0) + if (snprint_foreign_paths(reply, style, width) < 0) return 1; - if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len) + if (pretty && get_strbuf_len(reply) == (size_t)hdr_len) /* No output - clear header */ - truncate_strbuf(&reply, 0); + truncate_strbuf(reply, 0); - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_path (char ** r, int * len, struct vectors * vecs, struct path *pp, - char * style) +static int +show_path (struct strbuf *reply, struct vectors *vecs, struct path *pp, + char *style) { - STRBUF_ON_STACK(reply); + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; - get_path_layout(vecs->pathvec, 1); - if (snprint_path(&reply, style, pp, 0) < 0) + if ((width = alloc_path_layout()) == NULL) + return 1; + get_path_layout(vecs->pathvec, 1, width); + if (snprint_path(reply, style, pp, 0) < 0) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); - return 0; } -int -show_map_topology (char ** r, int * len, struct multipath * mpp, - struct vectors * vecs) +static int +show_map_topology (struct strbuf *reply, struct multipath *mpp, + struct vectors *vecs, const fieldwidth_t *width) { - STRBUF_ON_STACK(reply); - if (update_multipath(vecs, mpp->alias, 0)) return 1; - if (snprint_multipath_topology(&reply, mpp, 2) < 0) + if (snprint_multipath_topology(reply, mpp, 2, width) < 0) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_maps_topology (char ** r, int * len, struct vectors * vecs) +static int +show_maps_topology (struct strbuf *reply, struct vectors * vecs) { - STRBUF_ON_STACK(reply); int i; struct multipath * mpp; + fieldwidth_t *p_width __attribute__((cleanup(cleanup_ucharp))) = NULL; - get_path_layout(vecs->pathvec, 0); - foreign_path_layout(); + if ((p_width = alloc_path_layout()) == NULL) + return 1; + get_path_layout(vecs->pathvec, 0, p_width); + foreign_path_layout(p_width); vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { i--; continue; } - if (snprint_multipath_topology(&reply, mpp, 2) < 0) + if (snprint_multipath_topology(reply, mpp, 2, p_width) < 0) return 1; } - if (snprint_foreign_topology(&reply, 2) < 0) + if (snprint_foreign_topology(reply, 2, p_width) < 0) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_maps_json (char ** r, int * len, struct vectors * vecs) +static int +show_maps_json (struct strbuf *reply, struct vectors * vecs) { - STRBUF_ON_STACK(reply); int i; struct multipath * mpp; @@ -141,45 +128,38 @@ show_maps_json (char ** r, int * len, struct vectors * vecs) } } - if (snprint_multipath_topology_json(&reply, vecs) < 0) + if (snprint_multipath_topology_json(reply, vecs) < 0) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_map_json (char ** r, int * len, struct multipath * mpp, - struct vectors * vecs) +static int +show_map_json (struct strbuf *reply, struct multipath * mpp, + struct vectors * vecs) { - STRBUF_ON_STACK(reply); - if (update_multipath(vecs, mpp->alias, 0)) return 1; - if (snprint_multipath_map_json(&reply, mpp) < 0) + if (snprint_multipath_map_json(reply, mpp) < 0) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } static int -show_config (char ** r, int * len, const struct _vector *hwtable, +show_config (struct strbuf *reply, const struct _vector *hwtable, const struct _vector *mpvec) { struct config *conf; - char *reply; + int rc; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - reply = snprint_config(conf, len, hwtable, mpvec); + rc = __snprint_config(conf, reply, hwtable, mpvec); pthread_cleanup_pop(1); - if (reply == NULL) + if (rc < 0) return 1; - *r = reply; return 0; } @@ -194,12 +174,12 @@ reset_stats(struct multipath * mpp) mpp->stat_map_failures = 0; } -int -cli_list_config (void * v, char ** reply, int * len, void * data) +static int +cli_list_config (void *v, struct strbuf *reply, void *data) { condlog(3, "list config (operator)"); - return show_config(reply, len, NULL, NULL); + return show_config(reply, NULL, NULL); } static void v_free(void *x) @@ -207,10 +187,10 @@ static void v_free(void *x) vector_free(x); } -int -cli_list_config_local (void * v, char ** reply, int * len, void * data) +static int +cli_list_config_local (void *v, struct strbuf *reply, void *data) { - struct vectors * vecs = (struct vectors *)data; + struct vectors *vecs = (struct vectors *)data; vector hwes; int ret; @@ -218,45 +198,45 @@ cli_list_config_local (void * v, char ** reply, int * len, void * data) hwes = get_used_hwes(vecs->pathvec); pthread_cleanup_push(v_free, hwes); - ret = show_config(reply, len, hwes, vecs->mpvec); + ret = show_config(reply, hwes, vecs->mpvec); pthread_cleanup_pop(1); return ret; } -int -cli_list_paths (void * v, char ** reply, int * len, void * data) +static int +cli_list_paths (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list paths (operator)"); - return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1); + return show_paths(reply, vecs, PRINT_PATH_CHECKER, 1); } -int -cli_list_paths_fmt (void * v, char ** reply, int * len, void * data) +static int +cli_list_paths_fmt (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); - return show_paths(reply, len, vecs, fmt, 1); + return show_paths(reply, vecs, fmt, 1); } -int -cli_list_paths_raw (void * v, char ** reply, int * len, void * data) +static int +cli_list_paths_raw (void *v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); - return show_paths(reply, len, vecs, fmt, 0); + return show_paths(reply, vecs, fmt, 0); } -int -cli_list_path (void * v, char ** reply, int * len, void * data) +static int +cli_list_path (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -269,18 +249,21 @@ cli_list_path (void * v, char ** reply, int * len, void * data) if (!pp) return 1; - return show_path(reply, len, vecs, pp, "%o"); + return show_path(reply, vecs, pp, "%o"); } -int -cli_list_map_topology (void * v, char ** reply, int * len, void * data) +static int +cli_list_map_topology (void *v, struct strbuf *reply, void *data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); + fieldwidth_t *p_width __attribute__((cleanup(cleanup_ucharp))) = NULL; + if ((p_width = alloc_path_layout()) == NULL) + return 1; + get_path_layout(vecs->pathvec, 0, p_width); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -288,28 +271,27 @@ cli_list_map_topology (void * v, char ** reply, int * len, void * data) condlog(3, "list multipath %s (operator)", param); - return show_map_topology(reply, len, mpp, vecs); + return show_map_topology(reply, mpp, vecs, p_width); } -int -cli_list_maps_topology (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_topology (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list multipaths (operator)"); - return show_maps_topology(reply, len, vecs); + return show_maps_topology(reply, vecs); } -int -cli_list_map_json (void * v, char ** reply, int * len, void * data) +static int +cli_list_map_json (void *v, struct strbuf *reply, void *data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -317,86 +299,74 @@ cli_list_map_json (void * v, char ** reply, int * len, void * data) condlog(3, "list multipath json %s (operator)", param); - return show_map_json(reply, len, mpp, vecs); + return show_map_json(reply, mpp, vecs); } -int -cli_list_maps_json (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_json (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list multipaths json (operator)"); - return show_maps_json(reply, len, vecs); + return show_maps_json(reply, vecs); } -int -cli_list_wildcards (void * v, char ** reply, int * len, void * data) +static int +cli_list_wildcards (void *v, struct strbuf *reply, void *data) { - STRBUF_ON_STACK(buf); - - if (snprint_wildcards(&buf) < 0) + if (snprint_wildcards(reply) < 0) return 1; - *len = get_strbuf_len(&buf) + 1; - *reply = steal_strbuf_str(&buf); return 0; } -int -show_status (char ** r, int *len, struct vectors * vecs) +static int +show_status (struct strbuf *reply, struct vectors *vecs) { - STRBUF_ON_STACK(reply); - - if (snprint_status(&reply, vecs) < 0) + if (snprint_status(reply, vecs) < 0) return 1; - *len = get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_daemon (char ** r, int *len) +static int +show_daemon (struct strbuf *reply) { - STRBUF_ON_STACK(reply); - - if (print_strbuf(&reply, "pid %d %s\n", + if (print_strbuf(reply, "pid %d %s\n", daemon_pid, daemon_status()) < 0) return 1; - *len = get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_map (char ** r, int *len, struct multipath * mpp, char * style, - int pretty) +static int +show_map (struct strbuf *reply, struct multipath *mpp, char *style, + const fieldwidth_t *width) { - STRBUF_ON_STACK(reply); - - if (snprint_multipath(&reply, style, mpp, pretty) < 0) + if (snprint_multipath(reply, style, mpp, width) < 0) return 1; - *len = get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -show_maps (char ** r, int *len, struct vectors * vecs, char * style, +static int +show_maps (struct strbuf *reply, struct vectors *vecs, char *style, int pretty) { - STRBUF_ON_STACK(reply); int i; struct multipath * mpp; int hdr_len = 0; + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; - get_multipath_layout(vecs->mpvec, 1); - foreign_multipath_layout(); + if (pretty) { + if ((width = alloc_multipath_layout()) == NULL) + return 1; + get_multipath_layout(vecs->mpvec, 1, width); + foreign_multipath_layout(width); + } - if (pretty && (hdr_len = snprint_multipath_header(&reply, style)) < 0) + if (pretty && (hdr_len = snprint_multipath_header(reply, style, width)) < 0) return 1; vector_foreach_slot(vecs->mpvec, mpp, i) { @@ -404,133 +374,113 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style, i--; continue; } - if (snprint_multipath(&reply, style, mpp, pretty) < 0) + if (snprint_multipath(reply, style, mpp, width) < 0) return 1; } - if (snprint_foreign_multipaths(&reply, style, pretty) < 0) + if (snprint_foreign_multipaths(reply, style, width) < 0) return 1; - if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len) + if (pretty && get_strbuf_len(reply) == (size_t)hdr_len) /* No output - clear header */ - truncate_strbuf(&reply, 0); + truncate_strbuf(reply, 0); - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -cli_list_maps_fmt (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_fmt (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); - return show_maps(reply, len, vecs, fmt, 1); + return show_maps(reply, vecs, fmt, 1); } -int -cli_list_maps_raw (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_raw (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); - return show_maps(reply, len, vecs, fmt, 0); + return show_maps(reply, vecs, fmt, 0); } -int -cli_list_map_fmt (void * v, char ** reply, int * len, void * data) +static int +cli_list_map_fmt (void *v, struct strbuf *reply, void *data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); char * fmt = get_keyparam(v, FMT); + fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL; - param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); - get_multipath_layout(vecs->mpvec, 1); - mpp = find_mp_by_str(vecs->mpvec, param); - if (!mpp) + if ((width = alloc_multipath_layout()) == NULL) return 1; - - condlog(3, "list map %s fmt %s (operator)", param, fmt); - - return show_map(reply, len, mpp, fmt, 1); -} - -int -cli_list_map_raw (void * v, char ** reply, int * len, void * data) -{ - struct multipath * mpp; - struct vectors * vecs = (struct vectors *)data; - char * param = get_keyparam(v, MAP); - char * fmt = get_keyparam(v, FMT); - + get_multipath_layout(vecs->pathvec, 1, width); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); - get_multipath_layout(vecs->mpvec, 1); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list map %s fmt %s (operator)", param, fmt); - return show_map(reply, len, mpp, fmt, 0); + return show_map(reply, mpp, fmt, width); } -int -cli_list_maps (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1); + return show_maps(reply, vecs, PRINT_MAP_NAMES, 1); } -int -cli_list_status (void * v, char ** reply, int * len, void * data) +static int +cli_list_status (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list status (operator)"); - return show_status(reply, len, vecs); + return show_status(reply, vecs); } -int -cli_list_maps_status (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_status (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps status (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1); + return show_maps(reply, vecs, PRINT_MAP_STATUS, 1); } -int -cli_list_maps_stats (void * v, char ** reply, int * len, void * data) +static int +cli_list_maps_stats (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps stats (operator)"); - return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1); + return show_maps(reply, vecs, PRINT_MAP_STATS, 1); } -int -cli_list_daemon (void * v, char ** reply, int * len, void * data) +static int +cli_list_daemon (void *v, struct strbuf *reply, void *data) { condlog(3, "list daemon (operator)"); - return show_daemon(reply, len); + return show_daemon(reply); } -int -cli_reset_maps_stats (void * v, char ** reply, int * len, void * data) +static int +cli_reset_maps_stats (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; int i; @@ -544,8 +494,8 @@ cli_reset_maps_stats (void * v, char ** reply, int * len, void * data) return 0; } -int -cli_reset_map_stats (void * v, char ** reply, int * len, void * data) +static int +cli_reset_map_stats (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath * mpp; @@ -562,8 +512,35 @@ cli_reset_map_stats (void * v, char ** reply, int * len, void * data) return 0; } -int -cli_add_path (void * v, char ** reply, int * len, void * data) +static int +add_partial_path(struct path *pp, struct vectors *vecs) +{ + char wwid[WWID_SIZE]; + struct udev_device *udd; + + udd = get_udev_device(pp->dev_t, DEV_DEVT); + if (!udd) + return 0; + strcpy(wwid, pp->wwid); + if (get_uid(pp, pp->state, udd, 0) != 0) { + strcpy(pp->wwid, wwid); + udev_device_unref(udd); + return 0; + } + if (strlen(wwid) && strncmp(wwid, pp->wwid, WWID_SIZE) != 0) { + condlog(0, "%s: path wwid changed from '%s' to '%s'. removing", + pp->dev, wwid, pp->wwid); + ev_remove_path(pp, vecs, 1); + udev_device_unref(udd); + return -1; + } + udev_device_unref(pp->udev); + pp->udev = udd; + return finish_path_init(pp, vecs); +} + +static int +cli_add_path (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -587,8 +564,12 @@ cli_add_path (void * v, char ** reply, int * len, void * data) if (pp && pp->initialized != INIT_REMOVED) { condlog(2, "%s: path already in pathvec", param); - if (pp->recheck_wwid == RECHECK_WWID_ON && - check_path_wwid_change(pp)) { + if (pp->initialized == INIT_PARTIAL) { + if (add_partial_path(pp, vecs) < 0) + return 1; + } + else if (pp->recheck_wwid == RECHECK_WWID_ON && + check_path_wwid_change(pp)) { condlog(0, "%s: wwid changed. Removing device", pp->dev); handle_path_wwid_change(pp, vecs); @@ -673,13 +654,13 @@ cli_add_path (void * v, char ** reply, int * len, void * data) } return ev_add_path(pp, vecs, 1); blacklisted: - SET_REPLY_AND_LEN(reply, len, "blacklisted\n"); + append_strbuf_str(reply, "blacklisted\n"); condlog(2, "%s: path blacklisted", param); return 0; } -int -cli_del_path (void * v, char ** reply, int * len, void * data) +static int +cli_del_path (void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -695,14 +676,14 @@ cli_del_path (void * v, char ** reply, int * len, void * data) } ret = ev_remove_path(pp, vecs, 1); if (ret == REMOVE_PATH_DELAY) - SET_REPLY_AND_LEN(reply, len, "delayed\n"); + append_strbuf_str(reply, "delayed\n"); else if (ret == REMOVE_PATH_MAP_ERROR) - SET_REPLY_AND_LEN(reply, len, "map reload error. removed\n"); + append_strbuf_str(reply, "map reload error. removed\n"); return (ret == REMOVE_PATH_FAILURE); } -int -cli_add_map (void * v, char ** reply, int * len, void * data) +static int +cli_add_map (void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -722,7 +703,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data) invalid = 1; pthread_cleanup_pop(1); if (invalid) { - SET_REPLY_AND_LEN(reply, len, "blacklisted\n"); + append_strbuf_str(reply, "blacklisted\n"); condlog(2, "%s: map blacklisted", param); return 1; } @@ -746,7 +727,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data) != CP_OK) condlog(2, "%s: coalesce_paths failed", param); - FREE(refwwid); + free(refwwid); } } /*we attempt to create device only once*/ count++; @@ -757,12 +738,12 @@ cli_add_map (void * v, char ** reply, int * len, void * data) return 1; } rc = ev_add_map(dev_path, alias, vecs); - FREE(alias); + free(alias); return rc; } -int -cli_del_map (void * v, char ** reply, int * len, void * data) +static int +cli_del_map (void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -783,12 +764,15 @@ cli_del_map (void * v, char ** reply, int * len, void * data) return 1; } rc = ev_remove_map(param, alias, minor, vecs); - FREE(alias); + if (rc == 2) + append_strbuf_str(reply, "delayed"); + + free(alias); return rc; } -int -cli_del_maps (void *v, char **reply, int *len, void *data) +static int +cli_del_maps (void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; @@ -806,8 +790,8 @@ cli_del_maps (void *v, char **reply, int *len, void *data) return ret; } -int -cli_reload(void *v, char **reply, int *len, void *data) +static int +cli_reload(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); @@ -834,7 +818,7 @@ cli_reload(void *v, char **reply, int *len, void *data) return reload_and_sync_map(mpp, vecs, 0); } -int resize_map(struct multipath *mpp, unsigned long long size, +static int resize_map(struct multipath *mpp, unsigned long long size, struct vectors * vecs) { char *params __attribute__((cleanup(cleanup_charp))) = NULL; @@ -859,8 +843,8 @@ int resize_map(struct multipath *mpp, unsigned long long size, return 0; } -int -cli_resize(void *v, char **reply, int *len, void *data) +static int +cli_resize(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); @@ -924,8 +908,8 @@ cli_resize(void *v, char **reply, int *len, void *data) return 0; } -int -cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) +static int +cli_force_no_daemon_q(void * v, struct strbuf *reply, void * data) { struct config *conf; @@ -937,8 +921,8 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data) +static int +cli_restore_no_daemon_q(void * v, struct strbuf *reply, void * data) { struct config *conf; @@ -950,8 +934,8 @@ cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_restore_queueing(void *v, char **reply, int *len, void *data) +static int +cli_restore_queueing(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); @@ -991,8 +975,8 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data) return 0; } -int -cli_restore_all_queueing(void *v, char **reply, int *len, void *data) +static int +cli_restore_all_queueing(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; @@ -1013,8 +997,8 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data) return 0; } -int -cli_disable_queueing(void *v, char **reply, int *len, void *data) +static int +cli_disable_queueing(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); @@ -1042,8 +1026,8 @@ cli_disable_queueing(void *v, char **reply, int *len, void *data) return 0; } -int -cli_disable_all_queueing(void *v, char **reply, int *len, void *data) +static int +cli_disable_all_queueing(void *v, struct strbuf *reply, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; @@ -1061,8 +1045,8 @@ cli_disable_all_queueing(void *v, char **reply, int *len, void *data) return 0; } -int -cli_switch_group(void * v, char ** reply, int * len, void * data) +static int +cli_switch_group(void * v, struct strbuf *reply, void * data) { char * mapname = get_keyparam(v, MAP); int groupnum = atoi(get_keyparam(v, GROUP)); @@ -1073,25 +1057,26 @@ cli_switch_group(void * v, char ** reply, int * len, void * data) return dm_switchgroup(mapname, groupnum); } -int -cli_reconfigure(void * v, char ** reply, int * len, void * data) +static int +cli_reconfigure(void * v, struct strbuf *reply, void * data) { - int rc; - condlog(2, "reconfigure (operator)"); - rc = set_config_state(DAEMON_CONFIGURE); - if (rc == ETIMEDOUT) { - condlog(2, "timeout starting reconfiguration"); - return 1; - } else if (rc == EINVAL) - /* daemon shutting down */ - return 1; + schedule_reconfigure(FORCE_RELOAD_WEAK); return 0; } int -cli_suspend(void * v, char ** reply, int * len, void * data) +cli_reconfigure_all(void * v, struct strbuf *reply, void * data) +{ + condlog(2, "reconfigure all (operator)"); + + schedule_reconfigure(FORCE_RELOAD_YES); + return 0; +} + +static int +cli_suspend(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -1120,8 +1105,8 @@ cli_suspend(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_resume(void * v, char ** reply, int * len, void * data) +static int +cli_resume(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -1152,8 +1137,8 @@ cli_resume(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_reinstate(void * v, char ** reply, int * len, void * data) +static int +cli_reinstate(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -1175,8 +1160,8 @@ cli_reinstate(void * v, char ** reply, int * len, void * data) return dm_reinstate_path(pp->mpp->alias, pp->dev_t); } -int -cli_reassign (void * v, char ** reply, int * len, void * data) +static int +cli_reassign (void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); @@ -1199,8 +1184,8 @@ cli_reassign (void * v, char ** reply, int * len, void * data) return 0; } -int -cli_fail(void * v, char ** reply, int * len, void * data) +static int +cli_fail(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -1228,88 +1213,80 @@ cli_fail(void * v, char ** reply, int * len, void * data) return r; } -int -show_blacklist (char ** r, int * len) +static int +show_blacklist (struct strbuf *reply) { - STRBUF_ON_STACK(reply); struct config *conf; bool fail; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - fail = snprint_blacklist_report(conf, &reply) < 0; + fail = snprint_blacklist_report(conf, reply) < 0; pthread_cleanup_pop(1); if (fail) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); return 0; } -int -cli_list_blacklist (void * v, char ** reply, int * len, void * data) +static int +cli_list_blacklist (void * v, struct strbuf *reply, void * data) { condlog(3, "list blacklist (operator)"); - return show_blacklist(reply, len); + return show_blacklist(reply); } -int -show_devices (char ** r, int * len, struct vectors *vecs) +static int +show_devices (struct strbuf *reply, struct vectors *vecs) { - STRBUF_ON_STACK(reply); struct config *conf; bool fail; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - fail = snprint_devices(conf, &reply, vecs) < 0; + fail = snprint_devices(conf, reply, vecs) < 0; pthread_cleanup_pop(1); if (fail) return 1; - *len = (int)get_strbuf_len(&reply) + 1; - *r = steal_strbuf_str(&reply); - return 0; } -int -cli_list_devices (void * v, char ** reply, int * len, void * data) +static int +cli_list_devices (void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list devices (operator)"); - return show_devices(reply, len, vecs); + return show_devices(reply, vecs); } -int -cli_quit (void * v, char ** reply, int * len, void * data) +static int +cli_quit (void * v, struct strbuf *reply, void * data) { return 0; } -int -cli_shutdown (void * v, char ** reply, int * len, void * data) +static int +cli_shutdown (void * v, struct strbuf *reply, void * data) { condlog(3, "shutdown (operator)"); exit_daemon(); return 0; } -int -cli_getprstatus (void * v, char ** reply, int * len, void * data) +static int +cli_getprstatus (void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -1317,24 +1294,22 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data) condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag); - *len = asprintf(reply, "%d", mpp->prflag); - if (*len < 0) + if (print_strbuf(reply, "%d", mpp->prflag) < 0) return 1; - condlog(3, "%s: reply = %s", param, *reply); + condlog(3, "%s: reply = %s", param, get_strbuf_str(reply)); return 0; } -int -cli_setprstatus(void * v, char ** reply, int * len, void * data) +static int +cli_setprstatus(void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -1349,15 +1324,14 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_unsetprstatus(void * v, char ** reply, int * len, void * data) +static int +cli_unsetprstatus(void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); - get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) @@ -1371,8 +1345,8 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data) return 0; } -int -cli_getprkey(void * v, char ** reply, int * len, void * data) +static int +cli_getprkey(void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; @@ -1386,25 +1360,20 @@ cli_getprkey(void * v, char ** reply, int * len, void * data) if (!mpp) return 1; - *reply = malloc(26); - if (!*reply) - return 1; - key = get_be64(mpp->reservation_key); if (!key) { - sprintf(*reply, "none\n"); - *len = sizeof("none\n"); + append_strbuf_str(reply, "none\n"); return 0; } - /* This snprintf() can't overflow - PRIx64 needs max 16 chars */ - *len = snprintf(*reply, 26, "0x%" PRIx64 "%s\n", key, - mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") + 1; + if (print_strbuf(reply, "0x%" PRIx64 "%s\n", key, + mpp->sa_flags & MPATH_F_APTPL_MASK ? ":aptpl" : "") < 0) + return 1; return 0; } -int -cli_unsetprkey(void * v, char ** reply, int * len, void * data) +static int +cli_unsetprkey(void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; @@ -1427,8 +1396,8 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data) return ret; } -int -cli_setprkey(void * v, char ** reply, int * len, void * data) +static int +cli_setprkey(void * v, struct strbuf *reply, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; @@ -1459,7 +1428,7 @@ cli_setprkey(void * v, char ** reply, int * len, void * data) return ret; } -int cli_set_marginal(void * v, char ** reply, int * len, void * data) +static int cli_set_marginal(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -1486,7 +1455,7 @@ int cli_set_marginal(void * v, char ** reply, int * len, void * data) return reload_and_sync_map(pp->mpp, vecs, 0); } -int cli_unset_marginal(void * v, char ** reply, int * len, void * data) +static int cli_unset_marginal(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); @@ -1513,7 +1482,7 @@ int cli_unset_marginal(void * v, char ** reply, int * len, void * data) return reload_and_sync_map(pp->mpp, vecs, 0); } -int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data) +static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); @@ -1549,3 +1518,64 @@ int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data) return reload_and_sync_map(mpp, vecs, 0); } + +void init_handler_callbacks(void) +{ + set_handler_callback(LIST+PATHS, cli_list_paths); + set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); + set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); + set_handler_callback(LIST+PATH, cli_list_path); + set_handler_callback(LIST+MAPS, cli_list_maps); + set_handler_callback(LIST+STATUS, cli_list_status); + set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon); + set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); + set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); + set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); + set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); + set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); + set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json); + set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); + set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt); + set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt); + set_handler_callback(LIST+MAP+JSON, cli_list_map_json); + set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local); + set_handler_callback(LIST+CONFIG, cli_list_config); + set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); + set_handler_callback(LIST+DEVICES, cli_list_devices); + set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); + set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats); + set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats); + set_handler_callback(ADD+PATH, cli_add_path); + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); + set_handler_callback(DEL+MAPS, cli_del_maps); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); + set_unlocked_handler_callback(RECONFIGURE+ALL, cli_reconfigure_all); + set_handler_callback(SUSPEND+MAP, cli_suspend); + set_handler_callback(RESUME+MAP, cli_resume); + set_handler_callback(RESIZE+MAP, cli_resize); + set_handler_callback(RELOAD+MAP, cli_reload); + set_handler_callback(RESET+MAP, cli_reassign); + set_handler_callback(REINSTATE+PATH, cli_reinstate); + set_handler_callback(FAIL+PATH, cli_fail); + set_handler_callback(DISABLEQ+MAP, cli_disable_queueing); + set_handler_callback(RESTOREQ+MAP, cli_restore_queueing); + set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing); + set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing); + set_unlocked_handler_callback(QUIT, cli_quit); + set_unlocked_handler_callback(SHUTDOWN, cli_shutdown); + set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus); + set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus); + set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus); + set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q); + set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q); + set_handler_callback(GETPRKEY+MAP, cli_getprkey); + set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey); + set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); + set_handler_callback(SETMARGINAL+PATH, cli_set_marginal); + set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal); + set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal); +} diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h index 6f57b42..7eaf847 100644 --- a/multipathd/cli_handlers.h +++ b/multipathd/cli_handlers.h @@ -1,55 +1,6 @@ -int cli_list_paths (void * v, char ** reply, int * len, void * data); -int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data); -int cli_list_paths_raw (void * v, char ** reply, int * len, void * data); -int cli_list_path (void * v, char ** reply, int * len, void * data); -int cli_list_status (void * v, char ** reply, int * len, void * data); -int cli_list_daemon (void * v, char ** reply, int * len, void * data); -int cli_list_maps (void * v, char ** reply, int * len, void * data); -int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data); -int cli_list_maps_raw (void * v, char ** reply, int * len, void * data); -int cli_list_map_fmt (void * v, char ** reply, int * len, void * data); -int cli_list_map_raw (void * v, char ** reply, int * len, void * data); -int cli_list_maps_status (void * v, char ** reply, int * len, void * data); -int cli_list_maps_stats (void * v, char ** reply, int * len, void * data); -int cli_list_map_topology (void * v, char ** reply, int * len, void * data); -int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); -int cli_list_map_json (void * v, char ** reply, int * len, void * data); -int cli_list_maps_json (void * v, char ** reply, int * len, void * data); -int cli_list_config (void * v, char ** reply, int * len, void * data); -int cli_list_config_local (void * v, char ** reply, int * len, void * data); -int cli_list_blacklist (void * v, char ** reply, int * len, void * data); -int cli_list_devices (void * v, char ** reply, int * len, void * data); -int cli_list_wildcards (void * v, char ** reply, int * len, void * data); -int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data); -int cli_reset_map_stats (void * v, char ** reply, int * len, void * data); -int cli_add_path (void * v, char ** reply, int * len, void * data); -int cli_del_path (void * v, char ** reply, int * len, void * data); -int cli_add_map (void * v, char ** reply, int * len, void * data); -int cli_del_map (void * v, char ** reply, int * len, void * data); -int cli_del_maps (void * v, char ** reply, int * len, void * data); -int cli_switch_group(void * v, char ** reply, int * len, void * data); -int cli_reconfigure(void * v, char ** reply, int * len, void * data); -int cli_resize(void * v, char ** reply, int * len, void * data); -int cli_reload(void * v, char ** reply, int * len, void * data); -int cli_disable_queueing(void * v, char ** reply, int * len, void * data); -int cli_disable_all_queueing(void * v, char ** reply, int * len, void * data); -int cli_restore_queueing(void * v, char ** reply, int * len, void * data); -int cli_restore_all_queueing(void * v, char ** reply, int * len, void * data); -int cli_suspend(void * v, char ** reply, int * len, void * data); -int cli_resume(void * v, char ** reply, int * len, void * data); -int cli_reinstate(void * v, char ** reply, int * len, void * data); -int cli_fail(void * v, char ** reply, int * len, void * data); -int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data); -int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data); -int cli_quit(void * v, char ** reply, int * len, void * data); -int cli_shutdown(void * v, char ** reply, int * len, void * data); -int cli_reassign (void * v, char ** reply, int * len, void * data); -int cli_getprstatus(void * v, char ** reply, int * len, void * data); -int cli_setprstatus(void * v, char ** reply, int * len, void * data); -int cli_unsetprstatus(void * v, char ** reply, int * len, void * data); -int cli_getprkey(void * v, char ** reply, int * len, void * data); -int cli_setprkey(void * v, char ** reply, int * len, void * data); -int cli_unsetprkey(void * v, char ** reply, int * len, void * data); -int cli_set_marginal(void * v, char ** reply, int * len, void * data); -int cli_unset_marginal(void * v, char ** reply, int * len, void * data); -int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data); +#ifndef _CLI_HANDLERS_H +#define _CLI_HANDLERS_H + +void init_handler_callbacks(void); + +#endif diff --git a/multipathd/main.c b/multipathd/main.c index 3aff241..7a57a79 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -38,7 +38,6 @@ */ #include "parser.h" #include "vector.h" -#include "memory.h" #include "config.h" #include "util.h" #include "hwtable.h" @@ -127,6 +126,8 @@ static int poll_dmevents = 1; #endif /* Don't access this variable without holding config_lock */ static volatile enum daemon_status running_state = DAEMON_INIT; +/* Don't access this variable without holding config_lock */ +static bool __delayed_reconfig; pid_t daemon_pid; static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t config_cond; @@ -150,6 +151,16 @@ int should_exit(void) return get_running_state() == DAEMON_SHUTDOWN; } +static bool get_delayed_reconfig(void) +{ + bool val; + + pthread_mutex_lock(&config_lock); + val = __delayed_reconfig; + pthread_mutex_unlock(&config_lock); + return val; +} + /* * global copy of vecs for use in sig handlers */ @@ -210,14 +221,21 @@ static void do_sd_notify(enum daemon_status old_state, if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg)) sd_notify(0, notify_msg); - if (new_state == DAEMON_SHUTDOWN) + if (new_state == DAEMON_SHUTDOWN) { + /* Tell systemd that we're not RELOADING any more */ + if (old_state == DAEMON_CONFIGURE && startup_done) + sd_notify(0, "READY=1"); sd_notify(0, "STOPPING=1"); - else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) { + } else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) { sd_notify(0, "READY=1"); startup_done = true; } else if (new_state == DAEMON_CONFIGURE && startup_done) sd_notify(0, "RELOADING=1"); } +#else +static void do_sd_notify(__attribute__((unused)) enum daemon_status old_state, + __attribute__((unused)) enum daemon_status new_state) +{} #endif static void config_cleanup(__attribute__((unused)) void *arg) @@ -263,19 +281,53 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate, return st; } +/* Don't access this variable without holding config_lock */ +static enum force_reload_types reconfigure_pending = FORCE_RELOAD_NONE; +/* Only set while changing to DAEMON_CONFIGURE, and only access while + * reconfiguring or scheduling a delayed reconfig in DAEMON_CONFIGURE */ +static volatile enum force_reload_types reload_type = FORCE_RELOAD_NONE; + +static void enable_delayed_reconfig(void) +{ + pthread_mutex_lock(&config_lock); + reconfigure_pending = reload_type; + __delayed_reconfig = true; + pthread_mutex_unlock(&config_lock); +} + /* must be called with config_lock held */ static void __post_config_state(enum daemon_status state) { if (state != running_state && running_state != DAEMON_SHUTDOWN) { -#ifdef USE_SYSTEMD enum daemon_status old_state = running_state; -#endif + /* + * Handle a pending reconfigure request. + * DAEMON_IDLE is set from child() after reconfigure(), + * or from checkerloop() after completing checkers. + * In either case, child() will see DAEMON_CONFIGURE + * again and start another reconfigure cycle. + */ + if (reconfigure_pending != FORCE_RELOAD_NONE && + state == DAEMON_IDLE && + (old_state == DAEMON_CONFIGURE || + old_state == DAEMON_RUNNING)) { + /* + * notify systemd of transient idle state, lest systemd + * thinks the reload lasts forever. + */ + do_sd_notify(old_state, DAEMON_IDLE); + old_state = DAEMON_IDLE; + state = DAEMON_CONFIGURE; + } + if (state == DAEMON_CONFIGURE) { + reload_type = (reconfigure_pending == FORCE_RELOAD_YES) ? FORCE_RELOAD_YES : FORCE_RELOAD_WEAK; + reconfigure_pending = FORCE_RELOAD_NONE; + __delayed_reconfig = false; + } running_state = state; pthread_cond_broadcast(&config_cond); -#ifdef USE_SYSTEMD do_sd_notify(old_state, state); -#endif } } @@ -287,24 +339,54 @@ void post_config_state(enum daemon_status state) pthread_cleanup_pop(1); } -int set_config_state(enum daemon_status state) +void schedule_reconfigure(enum force_reload_types requested_type) +{ + pthread_mutex_lock(&config_lock); + pthread_cleanup_push(config_cleanup, NULL); + enum force_reload_types type; + + type = (reconfigure_pending == FORCE_RELOAD_YES || + requested_type == FORCE_RELOAD_YES) ? + FORCE_RELOAD_YES : FORCE_RELOAD_WEAK; + switch (running_state) + { + case DAEMON_SHUTDOWN: + break; + case DAEMON_IDLE: + reconfigure_pending = type; + __post_config_state(DAEMON_CONFIGURE); + break; + case DAEMON_CONFIGURE: + case DAEMON_RUNNING: + reconfigure_pending = type; + break; + default: + break; + } + pthread_cleanup_pop(1); +} + +static enum daemon_status set_config_state(enum daemon_status state) { int rc = 0; + enum daemon_status st; pthread_cleanup_push(config_cleanup, NULL); pthread_mutex_lock(&config_lock); - if (running_state != state) { - if (running_state == DAEMON_SHUTDOWN) - rc = EINVAL; - else - rc = __wait_for_state_change( - running_state != DAEMON_IDLE, 1000); - if (!rc) - __post_config_state(state); + while (rc == 0 && + running_state != state && + running_state != DAEMON_SHUTDOWN && + running_state != DAEMON_IDLE) { + rc = pthread_cond_wait(&config_cond, &config_lock); } + + if (rc == 0 && running_state == DAEMON_IDLE && state != DAEMON_IDLE) + __post_config_state(state); + st = running_state; + pthread_cleanup_pop(1); - return rc; + return st; } struct config *get_multipath_config(void) @@ -490,6 +572,8 @@ update_map (struct multipath *mpp, struct vectors *vecs, int new_map) { int retries = 3; char *params __attribute__((cleanup(cleanup_charp))) = NULL; + struct path *pp; + int i; retry: condlog(4, "%s: updating new map", mpp->alias); @@ -502,6 +586,15 @@ retry: verify_paths(mpp); 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*/ + mpath_pr_event_handle(pp); + } + } + } + if (setup_map(mpp, ¶ms, vecs)) { condlog(0, "%s: failed to setup new map in update", mpp->alias); retries = -1; @@ -541,11 +634,11 @@ add_map_without_path (struct vectors *vecs, const char *alias) if (!mpp) return NULL; if (!alias) { - FREE(mpp); + free(mpp); return NULL; } - mpp->alias = STRDUP(alias); + mpp->alias = strdup(alias); if (dm_get_info(mpp->alias, &mpp->dmi)) { condlog(3, "%s: cannot access table", mpp->alias); @@ -691,7 +784,7 @@ uev_add_map (struct uevent * uev, struct vectors * vecs) pthread_testcancel(); rc = ev_add_map(uev->kernel, alias, vecs); lock_cleanup_pop(vecs->lock); - FREE(alias); + free(alias); return rc; } @@ -704,7 +797,7 @@ int ev_add_map (char * dev, const char * alias, struct vectors * vecs) { struct multipath * mpp; - int delayed_reconfig, reassign_maps; + int reassign_maps; struct config *conf; if (dm_is_mpath(alias) != 1) { @@ -723,15 +816,14 @@ ev_add_map (char * dev, const char * alias, struct vectors * vecs) return 1; } conf = get_multipath_config(); - delayed_reconfig = conf->delayed_reconfig; reassign_maps = conf->reassign_maps; put_multipath_config(conf); if (mpp->wait_for_udev) { mpp->wait_for_udev = 0; - if (delayed_reconfig && + if (get_delayed_reconfig() && !need_to_delay_reconfig(vecs)) { condlog(2, "reconfigure (delayed)"); - set_config_state(DAEMON_CONFIGURE); + schedule_reconfigure(FORCE_RELOAD_WEAK); return 0; } } @@ -797,7 +889,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) remove_map_and_stop_waiter(mpp, vecs); out: lock_cleanup_pop(vecs->lock); - FREE(alias); + free(alias); return 0; } @@ -884,12 +976,19 @@ check_path_wwid_change(struct path *pp) return false; } +/* + * uev_add_path can call uev_update_path, and uev_update_path can call + * uev_add_path + */ +static int uev_update_path (struct uevent *uev, struct vectors * vecs); + static int uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) { struct path *pp; int ret = 0, i; struct config *conf; + bool partial_init = false; condlog(3, "%s: add path (uevent)", uev->kernel); if (strstr(uev->kernel, "..") != NULL) { @@ -908,7 +1007,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) int r; struct multipath *prev_mpp = NULL; - if (pp->initialized == INIT_REMOVED) { + if (pp->initialized == INIT_PARTIAL) { + partial_init = true; + goto out; + } else if (pp->initialized == INIT_REMOVED) { condlog(3, "%s: re-adding removed path", pp->dev); pp->initialized = INIT_NEW; prev_mpp = pp->mpp; @@ -1018,6 +1120,8 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) } out: lock_cleanup_pop(vecs->lock); + if (partial_init) + return uev_update_path(uev, vecs); return ret; } @@ -1313,6 +1417,28 @@ fail: return REMOVE_PATH_MAP_ERROR; } +int +finish_path_init(struct path *pp, struct vectors * vecs) +{ + int r; + struct config *conf; + + if (pp->udev && pp->uid_attribute && *pp->uid_attribute && + !udev_device_get_is_initialized(pp->udev)) + return 0; + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); + r = pathinfo(pp, conf, DI_ALL|DI_BLACKLIST); + pthread_cleanup_pop(1); + + if (r == PATHINFO_OK) + return 0; + + condlog(0, "%s: error fully initializing path, removing", pp->dev); + ev_remove_path(pp, vecs, 1); + return -1; +} + static int uev_update_path (struct uevent *uev, struct vectors * vecs) { @@ -1351,7 +1477,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) } /* Don't deal with other types of failed initialization * now. check_path will handle it */ - if (!strlen(pp->wwid)) + if (!strlen(pp->wwid) && pp->initialized != INIT_PARTIAL) goto out; strcpy(wwid, pp->wwid); @@ -1359,12 +1485,20 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) if (rc != 0) strcpy(pp->wwid, wwid); - else if (strncmp(wwid, pp->wwid, WWID_SIZE) != 0) { + else if (strlen(wwid) && + strncmp(wwid, pp->wwid, WWID_SIZE) != 0) { condlog(0, "%s: path wwid changed from '%s' to '%s'", uev->kernel, wwid, pp->wwid); ev_remove_path(pp, vecs, 1); needs_reinit = 1; goto out; + } else if (pp->initialized == INIT_PARTIAL) { + udev_device_unref(pp->udev); + pp->udev = udev_device_ref(uev->udev); + if (finish_path_init(pp, vecs) < 0) { + retval = 1; + goto out; + } } else { udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); @@ -1415,6 +1549,7 @@ out: condlog(0, "%s: spurious uevent, path not found", uev->kernel); } + /* pp->initalized must not be INIT_PARTIAL if needs_reinit is set */ if (needs_reinit) retval = uev_add_path(uev, vecs, 1); return retval; @@ -1450,11 +1585,11 @@ uev_pathfail_check(struct uevent *uev, struct vectors *vecs) pp->dev); out_lock: lock_cleanup_pop(vecs->lock); - FREE(devt); - FREE(action); + free(devt); + free(action); return r; out: - FREE(action); + free(action); return 1; } @@ -1476,48 +1611,6 @@ map_discovery (struct vectors * vecs) return 0; } -int -uxsock_trigger (char * str, char ** reply, int * len, bool is_root, - void * trigger_data) -{ - struct vectors * vecs; - int r; - - *reply = NULL; - *len = 0; - vecs = (struct vectors *)trigger_data; - - if ((str != NULL) && (is_root == false) && - (strncmp(str, "list", strlen("list")) != 0) && - (strncmp(str, "show", strlen("show")) != 0)) { - *reply = STRDUP("permission deny: need to be root"); - if (*reply) - *len = strlen(*reply) + 1; - return 1; - } - - r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000); - - if (r > 0) { - if (r == ETIMEDOUT) - *reply = STRDUP("timeout\n"); - else - *reply = STRDUP("fail\n"); - if (*reply) - *len = strlen(*reply) + 1; - r = 1; - } - else if (!r && *len == 0) { - *reply = STRDUP("ok\n"); - if (*reply) - *len = strlen(*reply) + 1; - r = 0; - } - /* else if (r < 0) leave *reply alone */ - - return r; -} - int uev_trigger (struct uevent * uev, void * trigger_data) { @@ -1643,65 +1736,18 @@ uxlsnrloop (void * ap) /* Tell main thread that thread has started */ post_config_state(DAEMON_CONFIGURE); - 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_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); - + init_handler_callbacks(); umask(077); - uxsock_listen(&uxsock_trigger, ux_sock, ap); + + /* + * Wait for initial reconfiguration to finish, while + * hadling signals + */ + while (wait_for_state_change_if(DAEMON_CONFIGURE, 50) + == DAEMON_CONFIGURE) + handle_signals(false); + + uxsock_listen(ux_sock, ap); out_sock: pthread_cleanup_pop(1); /* uxsock_cleanup */ @@ -1819,8 +1865,7 @@ missing_uev_wait_tick(struct vectors *vecs) { struct multipath * mpp; unsigned int i; - int timed_out = 0, delayed_reconfig; - struct config *conf; + int timed_out = 0; vector_foreach_slot (vecs->mpvec, mpp, i) { if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) { @@ -1836,13 +1881,10 @@ missing_uev_wait_tick(struct vectors *vecs) } } - conf = get_multipath_config(); - delayed_reconfig = conf->delayed_reconfig; - put_multipath_config(conf); - if (timed_out && delayed_reconfig && + if (timed_out && get_delayed_reconfig() && !need_to_delay_reconfig(vecs)) { condlog(2, "reconfigure (delayed)"); - set_config_state(DAEMON_CONFIGURE); + schedule_reconfigure(FORCE_RELOAD_WEAK); } } @@ -1906,6 +1948,25 @@ retry_count_tick(vector mpvec) } } +static void +partial_retrigger_tick(vector pathvec) +{ + struct path *pp; + unsigned int i; + + vector_foreach_slot (pathvec, pp, i) { + if (pp->initialized == INIT_PARTIAL && pp->udev && + pp->partial_retrigger_delay > 0 && + --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)); + } + } +} + int update_prio(struct path *pp, int refresh_all) { int oldpriority; @@ -2117,7 +2178,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) int marginal_pathgroups, marginal_changed = 0; int ret; - if (((pp->initialized == INIT_OK || + if (((pp->initialized == INIT_OK || pp->initialized == INIT_PARTIAL || pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) || pp->initialized == INIT_REMOVED) return 0; @@ -2481,6 +2542,10 @@ checkerloop (void *ap) int num_paths = 0, strict_timing, rc = 0; unsigned int ticks = 0; + 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); @@ -2496,13 +2561,6 @@ checkerloop (void *ap) if (use_watchdog) sd_notify(0, "WATCHDOG=1"); #endif - rc = set_config_state(DAEMON_RUNNING); - if (rc == ETIMEDOUT) { - condlog(4, "timeout waiting for DAEMON_IDLE"); - continue; - } else if (rc == EINVAL) - /* daemon shutdown */ - break; pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); @@ -2527,6 +2585,7 @@ checkerloop (void *ap) retry_count_tick(vecs->mpvec); missing_uev_wait_tick(vecs); ghost_delay_tick(vecs); + partial_retrigger_tick(vecs->pathvec); lock_cleanup_pop(vecs->lock); if (count) @@ -2610,7 +2669,6 @@ configure (struct vectors * vecs) vector mpvec; int i, ret; struct config *conf; - static int force_reload = FORCE_RELOAD_WEAK; if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) { condlog(0, "couldn't allocate path vec in configure"); @@ -2658,15 +2716,7 @@ configure (struct vectors * vecs) if (should_exit()) goto fail; - /* - * create new set of maps & push changed ones into dm - * In the first call, use FORCE_RELOAD_WEAK to avoid making - * superfluous ACT_RELOAD ioctls. Later calls are done - * with FORCE_RELOAD_YES. - */ - ret = coalesce_paths(vecs, mpvec, NULL, force_reload, CMD_NONE); - if (force_reload == FORCE_RELOAD_WEAK) - force_reload = FORCE_RELOAD_YES; + ret = coalesce_paths(vecs, mpvec, NULL, reload_type, CMD_NONE); if (ret != CP_OK) { condlog(0, "configure failed while coalescing paths"); goto fail; @@ -2769,7 +2819,9 @@ reconfigure (struct vectors * vecs) reset_checker_classes(); if (bindings_read_only) conf->bindings_read_only = bindings_read_only; - check_alias_settings(conf); + + if (check_alias_settings(conf)) + return 1; uxsock_timeout = conf->uxsock_timeout; @@ -2789,7 +2841,7 @@ init_vecs (void) { struct vectors * vecs; - vecs = (struct vectors *)MALLOC(sizeof(struct vectors)); + vecs = (struct vectors *)calloc(1, sizeof(struct vectors)); if (!vecs) return NULL; @@ -2830,7 +2882,7 @@ handle_signals(bool nonfatal) return; if (reconfig_sig) { condlog(2, "reconfigure (signal)"); - set_config_state(DAEMON_CONFIGURE); + schedule_reconfigure(FORCE_RELOAD_WEAK); } if (log_reset_sig) { condlog(2, "reset log (signal)"); @@ -2900,51 +2952,28 @@ setscheduler (void) return; } -static void -set_oom_adj (void) +static void set_oom_adj(void) { -#ifdef OOM_SCORE_ADJ_MIN - int retry = 1; - char *file = "/proc/self/oom_score_adj"; - int score = OOM_SCORE_ADJ_MIN; -#else - int retry = 0; - char *file = "/proc/self/oom_adj"; - int score = OOM_ADJUST_MIN; -#endif FILE *fp; - struct stat st; - char *envp; - envp = getenv("OOMScoreAdjust"); - if (envp) { + if (getenv("OOMScoreAdjust")) { condlog(3, "Using systemd provided OOMScoreAdjust"); return; } - do { - if (stat(file, &st) == 0){ - fp = fopen(file, "w"); - if (!fp) { - condlog(0, "couldn't fopen %s : %s", file, - strerror(errno)); - return; - } - fprintf(fp, "%i", score); - fclose(fp); - return; - } - if (errno != ENOENT) { - condlog(0, "couldn't stat %s : %s", file, - strerror(errno)); - return; - } -#ifdef OOM_ADJUST_MIN - file = "/proc/self/oom_adj"; - score = OOM_ADJUST_MIN; -#else - retry = 0; +#ifdef OOM_SCORE_ADJ_MIN + fp = fopen("/proc/self/oom_score_adj", "w"); + if (fp) { + fprintf(fp, "%i", OOM_SCORE_ADJ_MIN); + fclose(fp); + return; + } #endif - } while (retry--); + fp = fopen("/proc/self/oom_adj", "w"); + if (fp) { + fprintf(fp, "%i", OOM_ADJUST_MIN); + fclose(fp); + return; + } condlog(0, "couldn't adjust oom score"); } @@ -3001,7 +3030,8 @@ static void cleanup_vecs(void) cleanup_maps(gvecs); cleanup_paths(gvecs); pthread_mutex_destroy(&gvecs->lock.mutex); - FREE(gvecs); + free(gvecs); + gvecs = NULL; } static void cleanup_threads(void) @@ -3099,10 +3129,6 @@ static void cleanup_child(void) log_thread_stop(); cleanup_conf(); - -#ifdef _DEBUG_ - dbg_free_final(NULL); -#endif } static int sd_notify_exit(int err) @@ -3285,18 +3311,22 @@ child (__attribute__((unused)) void *param) if (state == DAEMON_SHUTDOWN) break; if (state == DAEMON_CONFIGURE) { + int rc = 0; + pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); - if (!need_to_delay_reconfig(vecs)) { - reconfigure(vecs); - } else { - conf = get_multipath_config(); - conf->delayed_reconfig = 1; - put_multipath_config(conf); - } + if (!need_to_delay_reconfig(vecs)) + rc = reconfigure(vecs); + else + enable_delayed_reconfig(); lock_cleanup_pop(vecs->lock); - post_config_state(DAEMON_IDLE); + if (!rc) + post_config_state(DAEMON_IDLE); + else { + condlog(0, "fatal error applying configuration - aborting"); + exit_daemon(); + } } } @@ -3307,11 +3337,18 @@ failed: return sd_notify_exit(exit_code); } +static void cleanup_close(int *pfd) +{ + if (*pfd != -1 && *pfd != STDIN_FILENO && *pfd != STDOUT_FILENO && + *pfd != STDERR_FILENO) + close(*pfd); +} + static int daemonize(void) { int pid; - int dev_null_fd; + int dev_null_fd __attribute__((cleanup(cleanup_close))) = -1; if( (pid = fork()) < 0){ fprintf(stderr, "Failed first fork : %s\n", strerror(errno)); @@ -3337,25 +3374,21 @@ daemonize(void) _exit(0); } - close(STDIN_FILENO); - if (dup(dev_null_fd) < 0) { - fprintf(stderr, "cannot dup /dev/null to stdin : %s\n", + if (dup2(dev_null_fd, STDIN_FILENO) < 0) { + fprintf(stderr, "cannot dup2 /dev/null to stdin : %s\n", strerror(errno)); _exit(0); } - close(STDOUT_FILENO); - if (dup(dev_null_fd) < 0) { - fprintf(stderr, "cannot dup /dev/null to stdout : %s\n", + if (dup2(dev_null_fd, STDOUT_FILENO) < 0) { + fprintf(stderr, "cannot dup2 /dev/null to stdout : %s\n", strerror(errno)); _exit(0); } - close(STDERR_FILENO); - if (dup(dev_null_fd) < 0) { + if (dup2(dev_null_fd, STDERR_FILENO) < 0) { fprintf(stderr, "cannot dup /dev/null to stderr : %s\n", strerror(errno)); _exit(0); } - close(dev_null_fd); daemon_pid = getpid(); return 0; } @@ -3379,11 +3412,6 @@ main (int argc, char *argv[]) logsink = LOGSINK_SYSLOG; - if (getuid() != 0) { - fprintf(stderr, "need to be root\n"); - exit(1); - } - /* make sure we don't lock any path */ if (chdir("/") < 0) fprintf(stderr, "can't chdir to root directory : %s\n", @@ -3470,6 +3498,11 @@ main (int argc, char *argv[]) return err; } + if (getuid() != 0) { + fprintf(stderr, "need to be root\n"); + exit(1); + } + if (foreground) { if (!isatty(fileno(stdout))) setbuf(stdout, NULL); @@ -3544,7 +3577,7 @@ void * mpath_pr_event_handler_fn (void * pathp ) goto out; } - param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor)); + param = (struct prout_param_descriptor *)calloc(1, sizeof(struct prout_param_descriptor)); if (!param) goto out; diff --git a/multipathd/main.h b/multipathd/main.h index bc1f938..8356b25 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -37,14 +37,13 @@ void exit_daemon(void); const char * daemon_status(void); enum daemon_status wait_for_state_change_if(enum daemon_status oldstate, unsigned long ms); +void schedule_reconfigure(enum force_reload_types requested_type); int need_to_delay_reconfig (struct vectors *); -int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *, int); int ev_remove_path (struct path *, struct vectors *, int); int ev_add_map (char *, const char *, struct vectors *); int ev_remove_map (char *, char *, int, struct vectors *); int flush_map(struct multipath *, struct vectors *, int); -int set_config_state(enum daemon_status); void * mpath_alloc_prin_response(int prin_sa); int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, int noisy); @@ -66,4 +65,5 @@ int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs, void handle_path_wwid_change(struct path *pp, struct vectors *vecs); bool check_path_wwid_change(struct path *pp); +int finish_path_init(struct path *pp, struct vectors * vecs); #endif /* MAIN_H */ diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index 048a838..293e7f2 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -55,7 +55,7 @@ Suppress timestamps. Do not prefix logging messages with a timestamp. . .TP .BI \-v " level" -Verbosity level. Print additional information while running multipathd. A level +Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. . @@ -195,10 +195,16 @@ group index, starting with 1. . .TP .B reconfigure -Reconfigures the multipaths. This should be triggered automatically after anyi -hotplug event. +Rereads the configuration, and reloads all changed multipath devices. This +also happens at startup, when the service is reload, or when a SIGHUP is +received. . .TP +.B reconfigure all +Rereads the configuration, and reloads all multipath devices regardless of +whether or not they have changed. This also happens when \fImultipath -r\fR is +run. +.TP .B suspend map|multipath $map Sets map $map into suspend state. . @@ -208,7 +214,7 @@ Resumes map $map from suspend state. . .TP .B reset map|multipath $map -Reassign existing device-mapper table(s) use use the multipath device, instead +Reassign existing device-mapper table(s) use the multipath device, instead of its path devices. . .TP diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service index 0b2ac81..1919b38 100644 --- a/multipathd/multipathd.service +++ b/multipathd/multipathd.service @@ -1,9 +1,10 @@ [Unit] Description=Device-Mapper Multipath Device Controller -Wants=systemd-udev-trigger.service systemd-udev-settle.service Before=iscsi.service iscsid.service lvm2-activation-early.service Before=local-fs-pre.target blk-availability.service shutdown.target -After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service +Wants=systemd-udevd-kernel.socket +After=systemd-udevd-kernel.socket +After=multipathd.socket systemd-remount-fs.service DefaultDependencies=no Conflicts=shutdown.target ConditionKernelCommandLine=!nompath diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket index 0ed4a1f..c777e5e 100644 --- a/multipathd/multipathd.socket +++ b/multipathd/multipathd.socket @@ -1,6 +1,9 @@ [Unit] Description=multipathd control socket DefaultDependencies=no +ConditionKernelCommandLine=!nompath +ConditionKernelCommandLine=!multipath=off +ConditionVirtualization=!container Before=sockets.target [Socket] diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c index a76f8e2..b1b058b 100644 --- a/multipathd/uxclnt.c +++ b/multipathd/uxclnt.c @@ -20,7 +20,6 @@ #include "mpath_cmd.h" #include "uxsock.h" -#include "memory.h" #include "defaults.h" #include "vector.h" @@ -99,7 +98,7 @@ static void process(int fd, unsigned int timeout) add_history(line); free(line); - FREE(reply); + free(reply); } } @@ -122,7 +121,7 @@ static int process_req(int fd, char * inbuf, unsigned int timeout) } else { printf("%s", reply); ret = (strcmp(reply, "fail\n") == 0); - FREE(reply); + free(reply); return ret; } } diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index dbee0d6..912ac3c 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -24,8 +24,8 @@ #include #include #include +#include #include "checkers.h" -#include "memory.h" #include "debug.h" #include "vector.h" #include "structs.h" @@ -40,14 +40,40 @@ #include "main.h" #include "cli.h" #include "uxlsnr.h" +#include "strbuf.h" + +/* state of client connection */ +enum { + CLT_RECV, + CLT_PARSE, + CLT_LOCKED_WORK, + CLT_WORK, + CLT_SEND, +}; struct client { struct list_head node; + struct timespec expires; + int state; int fd; + vector cmdvec; + /* NUL byte at end */ + char cmd[_MAX_CMD_LEN + 1]; + struct strbuf reply; + struct handler *handler; + size_t cmd_len, len; + int error; + bool is_root; +}; + +/* Indices for array of poll fds */ +enum { + POLLFD_UX = 0, + POLLFD_NOTIFY, + POLLFD_IDLE, + POLLFDS_BASE, }; -/* The number of fds we poll on, other than individual client connections */ -#define POLLFDS_BASE 2 #define POLLFD_CHUNK (4096 / sizeof(struct pollfd)) /* Minimum mumber of pollfds to reserve for clients */ #define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE) @@ -62,13 +88,11 @@ struct client { static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)]; static LIST_HEAD(clients); -static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; static struct pollfd *polls; static int notify_fd = -1; +static int idle_fd = -1; static char *watch_config_dir; -static bool _socket_client_is_root(int fd); - static bool _socket_client_is_root(int fd) { socklen_t len = 0; @@ -99,64 +123,40 @@ static void new_client(int ux_sock) if (fd == -1) return; - c = (struct client *)MALLOC(sizeof(*c)); + c = (struct client *)calloc(1, sizeof(*c)); if (!c) { close(fd); return; } - memset(c, 0, sizeof(*c)); INIT_LIST_HEAD(&c->node); c->fd = fd; + c->state = CLT_RECV; + c->is_root = _socket_client_is_root(c->fd); /* put it in our linked list */ - pthread_mutex_lock(&client_lock); list_add_tail(&c->node, &clients); - pthread_mutex_unlock(&client_lock); } /* * kill off a dead client */ -static void _dead_client(struct client *c) +static void dead_client(struct client *c) { int fd = c->fd; list_del_init(&c->node); c->fd = -1; - FREE(c); + reset_strbuf(&c->reply); + if (c->cmdvec) + free_keys(c->cmdvec); + free(c); close(fd); } -static void dead_client(struct client *c) -{ - pthread_cleanup_push(cleanup_mutex, &client_lock); - pthread_mutex_lock(&client_lock); - _dead_client(c); - pthread_cleanup_pop(1); -} - static void free_polls (void) { if (polls) - FREE(polls); -} - -static void check_timeout(struct timespec start_time, char *inbuf, - unsigned int timeout) -{ - struct timespec diff_time, end_time; - - if (start_time.tv_sec) { - unsigned long msecs; - - get_monotonic_time(&end_time); - timespecsub(&end_time, &start_time, &diff_time); - msecs = diff_time.tv_sec * 1000 + - diff_time.tv_nsec / (1000 * 1000); - if (msecs > timeout) - condlog(2, "cli cmd '%s' timeout reached " - "after %ld.%06lu secs", inbuf, - (long)diff_time.tv_sec, diff_time.tv_nsec / 1000); - } + free(polls); + polls = NULL; } void uxsock_cleanup(void *arg) @@ -169,16 +169,25 @@ void uxsock_cleanup(void *arg) close(notify_fd); free(watch_config_dir); - pthread_mutex_lock(&client_lock); list_for_each_entry_safe(client_loop, client_tmp, &clients, node) { - _dead_client(client_loop); + dead_client(client_loop); } - pthread_mutex_unlock(&client_lock); cli_exit(); free_polls(); } +void wakeup_cleanup(void *arg) +{ + struct mutex_lock *lck = arg; + int fd = idle_fd; + + idle_fd = -1; + set_wakeup_fn(lck, NULL); + if (fd != -1) + close(fd); +} + struct watch_descriptors { int conf_wd; int dir_wd; @@ -283,30 +292,337 @@ static void handle_inotify(int fd, struct watch_descriptors *wds) condlog(1, "Multipath configuration updated.\nReload multipathd for changes to take effect"); } +static const struct timespec ts_zero = { .tv_sec = 0, }; +static const struct timespec ts_max = { .tv_sec = LONG_MAX, .tv_nsec = 999999999 }; + +static struct timespec *get_soonest_timeout(struct timespec *ts) +{ + struct timespec ts_min = ts_max, now; + bool any = false; + struct client *c; + + list_for_each_entry(c, &clients, node) { + if (timespeccmp(&c->expires, &ts_zero) != 0 && + timespeccmp(&c->expires, &ts_min) < 0) { + ts_min = c->expires; + any = true; + } + } + + if (!any) + return NULL; + + get_monotonic_time(&now); + timespecsub(&ts_min, &now, ts); + if (timespeccmp(ts, &ts_zero) < 0) + *ts = ts_zero; + + condlog(4, "%s: next client expires in %ld.%03lds", __func__, + (long)ts->tv_sec, ts->tv_nsec / 1000000); + return ts; +} + +static bool need_vecs_lock(void) +{ + struct client *c; + + list_for_each_entry(c, &clients, node) { + if (c->state == CLT_LOCKED_WORK) + return true; + } + return false; +} + +static int parse_cmd(struct client *c) +{ + int r; + + r = get_cmdvec(c->cmd, &c->cmdvec); + + if (r) + return -r; + + c->handler = find_handler_for_cmdvec(c->cmdvec); + + if (!c->handler || !c->handler->fn) + return -EINVAL; + + return r; +} + +static int execute_handler(struct client *c, struct vectors *vecs) +{ + + if (!c->handler || !c->handler->fn) + return -EINVAL; + + return c->handler->fn(c->cmdvec, &c->reply, vecs); +} + +static void wakeup_listener(void) +{ + uint64_t one = 1; + + if (idle_fd != -1 && + write(idle_fd, &one, sizeof(one)) != sizeof(one)) + condlog(1, "%s: failed", __func__); +} + +static void drain_idle_fd(int fd) +{ + uint64_t val; + int rc; + + rc = read(fd, &val, sizeof(val)); + condlog(4, "%s: %d, %"PRIu64, __func__, rc, val); +} + +void default_reply(struct client *c, int r) +{ + switch(r) { + case -EINVAL: + case -ESRCH: + case -ENOMEM: + /* return codes from get_cmdvec() */ + genhelp_handler(c->cmd, -r, &c->reply); + break; + case -EPERM: + append_strbuf_str(&c->reply, + "permission deny: need to be root\n"); + break; + case -ETIMEDOUT: + append_strbuf_str(&c->reply, "timeout\n"); + break; + case 0: + append_strbuf_str(&c->reply, "ok\n"); + break; + default: + /* cli_handler functions return 1 on unspecified error */ + append_strbuf_str(&c->reply, "fail\n"); + break; + } +} + +static void set_client_state(struct client *c, int state) +{ + switch(state) + { + case CLT_RECV: + reset_strbuf(&c->reply); + memset(c->cmd, '\0', sizeof(c->cmd)); + c->expires = ts_zero; + c->error = 0; + /* fallthrough */ + case CLT_SEND: + /* reuse these fields for next data transfer */ + c->len = c->cmd_len = 0; + break; + default: + break; + } + c->state = state; +} + +enum { + STM_CONT, + STM_BREAK, +}; + +static int client_state_machine(struct client *c, struct vectors *vecs, + short revents) +{ + ssize_t n; + + condlog(4, "%s: cli[%d] poll=%x state=%d cmd=\"%s\" repl \"%s\"", __func__, + c->fd, revents, c->state, c->cmd, get_strbuf_str(&c->reply)); + + switch (c->state) { + case CLT_RECV: + if (!(revents & POLLIN)) + return STM_BREAK; + if (c->cmd_len == 0) { + size_t len; + /* + * We got POLLIN; assume that at least the length can + * be read immediately. + */ + get_monotonic_time(&c->expires); + c->expires.tv_sec += uxsock_timeout / 1000; + c->expires.tv_nsec += (uxsock_timeout % 1000) * 1000000; + normalize_timespec(&c->expires); + n = recv(c->fd, &len, sizeof(len), 0); + if (n < (ssize_t)sizeof(len)) { + condlog(1, "%s: cli[%d]: failed to receive reply len: %zd", + __func__, c->fd, n); + c->error = -ECONNRESET; + } else if (len <= 0 || len > _MAX_CMD_LEN) { + condlog(1, "%s: cli[%d]: invalid command length (%zu bytes)", + __func__, c->fd, len); + c->error = -ECONNRESET; + } else { + c->cmd_len = len; + condlog(4, "%s: cli[%d]: connected", __func__, c->fd); + } + /* poll for data */ + return STM_BREAK; + } else if (c->len < c->cmd_len) { + n = recv(c->fd, c->cmd + c->len, c->cmd_len - c->len, 0); + if (n <= 0 && errno != EINTR && errno != EAGAIN) { + condlog(1, "%s: cli[%d]: error in recv: %m", + __func__, c->fd); + c->error = -ECONNRESET; + return STM_BREAK; + } + c->len += n; + if (c->len < c->cmd_len) + /* continue polling */ + return STM_BREAK; + } + condlog(4, "cli[%d]: Got request [%s]", c->fd, c->cmd); + set_client_state(c, CLT_PARSE); + return STM_CONT; + + case CLT_PARSE: + c->error = parse_cmd(c); + if (!c->error) { + /* Permission check */ + struct key *kw = VECTOR_SLOT(c->cmdvec, 0); + + if (!c->is_root && kw->code != LIST) { + c->error = -EPERM; + condlog(0, "%s: cli[%d]: unauthorized cmd \"%s\"", + __func__, c->fd, c->cmd); + } + } + if (c->error) + set_client_state(c, CLT_SEND); + else if (c->handler->locked) + set_client_state(c, CLT_LOCKED_WORK); + else + set_client_state(c, CLT_WORK); + return STM_CONT; + + case CLT_LOCKED_WORK: + if (trylock(&vecs->lock) == 0) { + /* don't use cleanup_lock(), lest we wakeup ourselves */ + pthread_cleanup_push_cast(__unlock, &vecs->lock); + c->error = execute_handler(c, vecs); + pthread_cleanup_pop(1); + condlog(4, "%s: cli[%d] grabbed lock", __func__, c->fd); + free_keys(c->cmdvec); + c->cmdvec = NULL; + set_client_state(c, CLT_SEND); + /* Wait for POLLOUT */ + return STM_BREAK; + } else { + condlog(4, "%s: cli[%d] waiting for lock", __func__, c->fd); + return STM_BREAK; + } + + case CLT_WORK: + c->error = execute_handler(c, vecs); + free_keys(c->cmdvec); + c->cmdvec = NULL; + set_client_state(c, CLT_SEND); + /* Wait for POLLOUT */ + return STM_BREAK; + + case CLT_SEND: + if (get_strbuf_len(&c->reply) == 0) + default_reply(c, c->error); + + if (c->cmd_len == 0) { + size_t len = get_strbuf_len(&c->reply) + 1; + + if (send(c->fd, &len, sizeof(len), MSG_NOSIGNAL) + != sizeof(len)) + c->error = -ECONNRESET; + c->cmd_len = len; + return STM_BREAK; + } + + if (c->len < c->cmd_len) { + const char *buf = get_strbuf_str(&c->reply); + + n = send(c->fd, buf + c->len, c->cmd_len, MSG_NOSIGNAL); + if (n == -1) { + if (!(errno == EAGAIN || errno == EINTR)) + c->error = -ECONNRESET; + } else + c->len += n; + } + + if (c->len >= c->cmd_len) { + condlog(4, "cli[%d]: Reply [%zu bytes]", c->fd, c->cmd_len); + set_client_state(c, CLT_RECV); + } + return STM_BREAK; + + default: + return STM_BREAK; + } +} + +static void check_timeout(struct client *c) +{ + struct timespec now; + + if (timespeccmp(&c->expires, &ts_zero) == 0) + return; + + get_monotonic_time(&now); + if (timespeccmp(&c->expires, &now) > 0) + return; + + condlog(2, "%s: cli[%d]: timed out at %ld.%03ld", __func__, + c->fd, (long)c->expires.tv_sec, c->expires.tv_nsec / 1000000); + + c->error = -ETIMEDOUT; + set_client_state(c, CLT_SEND); +} + +static void handle_client(struct client *c, struct vectors *vecs, short revents) +{ + if (revents & (POLLHUP|POLLERR)) { + c->error = -ECONNRESET; + return; + } + + check_timeout(c); + while (client_state_machine(c, vecs, revents) == STM_CONT); +} + /* * entry point */ -void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, - void * trigger_data) +void *uxsock_listen(long ux_sock, void *trigger_data) { - int rlen; - char *inbuf; - char *reply; sigset_t mask; int max_pfds = MIN_POLLS + POLLFDS_BASE; /* conf->sequence_nr will be 1 when uxsock_listen is first called */ unsigned int sequence_nr = 0; struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 }; + struct vectors *vecs = trigger_data; condlog(3, "uxsock: startup listener"); - polls = MALLOC(max_pfds * sizeof(*polls)); + polls = calloc(1, max_pfds * sizeof(*polls)); if (!polls) { condlog(0, "uxsock: failed to allocate poll fds"); exit_daemon(); + return NULL; } notify_fd = inotify_init1(IN_NONBLOCK); if (notify_fd == -1) /* it's fine if notifications fail */ condlog(3, "failed to start up configuration notifications"); + + pthread_cleanup_push(wakeup_cleanup, &vecs->lock); + idle_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); + if (idle_fd == -1) { + condlog(1, "failed to create idle fd"); + exit_daemon(); + } else + set_wakeup_fn(&vecs->lock, wakeup_listener); + sigfillset(&mask); sigdelset(&mask, SIGINT); sigdelset(&mask, SIGTERM); @@ -315,10 +631,9 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, while (1) { struct client *c, *tmp; int i, n_pfds, poll_count, num_clients; + struct timespec __timeout, *timeout; /* setup for a poll */ - pthread_mutex_lock(&client_lock); - pthread_cleanup_push(cleanup_mutex, &client_lock); num_clients = 0; list_for_each_entry(c, &clients, node) { num_clients++; @@ -327,7 +642,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, struct pollfd *new; int n_new = max_pfds + POLLFD_CHUNK; - new = REALLOC(polls, n_new * sizeof(*polls)); + new = realloc(polls, n_new * sizeof(*polls)); if (new) { max_pfds = n_new; polls = new; @@ -339,8 +654,8 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, } } if (num_clients < MAX_CLIENTS) { - polls[0].fd = ux_sock; - polls[0].events = POLLIN; + polls[POLLFD_UX].fd = ux_sock; + polls[POLLFD_UX].events = POLLIN; } else { /* * New clients can't connect, num_clients won't grow @@ -348,30 +663,46 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, */ condlog(1, "%s: max client connections reached, pausing polling", __func__); - polls[0].fd = -1; + polls[POLLFD_UX].fd = -1; } reset_watch(notify_fd, &wds, &sequence_nr); + polls[POLLFD_NOTIFY].fd = notify_fd; if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1)) - polls[1].fd = -1; + polls[POLLFD_NOTIFY].events = 0; else - polls[1].fd = notify_fd; - polls[1].events = POLLIN; + polls[POLLFD_NOTIFY].events = POLLIN; + + polls[POLLFD_IDLE].fd = idle_fd; + if (need_vecs_lock()) + polls[POLLFD_IDLE].events = POLLIN; + else + polls[POLLFD_IDLE].events = 0; /* setup the clients */ i = POLLFDS_BASE; list_for_each_entry(c, &clients, node) { + switch(c->state) { + case CLT_RECV: + polls[i].events = POLLIN; + break; + case CLT_SEND: + polls[i].events = POLLOUT; + break; + default: + /* don't poll for this client */ + continue; + } polls[i].fd = c->fd; - polls[i].events = POLLIN; i++; if (i >= max_pfds) break; } n_pfds = i; - pthread_cleanup_pop(1); + timeout = get_soonest_timeout(&__timeout); /* most of our life is spent in this call */ - poll_count = ppoll(polls, n_pfds, NULL, &mask); + poll_count = ppoll(polls, n_pfds, timeout, &mask); handle_signals(false); if (poll_count == -1) { @@ -386,88 +717,43 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, break; } - if (poll_count == 0) { - handle_signals(true); - continue; - } + if (polls[POLLFD_IDLE].fd != -1 && + polls[POLLFD_IDLE].revents & POLLIN) + drain_idle_fd(idle_fd); - /* - * Client connection. We shouldn't answer while we're - * configuring - nothing may be configured yet. - * But we can't wait forever either, because this thread - * must handle signals. So wait a short while only. - */ - if (wait_for_state_change_if(DAEMON_CONFIGURE, 10) - == DAEMON_CONFIGURE) { - handle_signals(false); - continue; - } + /* see if a client needs handling */ + list_for_each_entry_safe(c, tmp, &clients, node) { + short revents = 0; - /* see if a client wants to speak to us */ - for (i = POLLFDS_BASE; i < n_pfds; i++) { - if (polls[i].revents & POLLIN) { - struct timespec start_time; - - c = NULL; - pthread_mutex_lock(&client_lock); - list_for_each_entry(tmp, &clients, node) { - if (tmp->fd == polls[i].fd) { - c = tmp; - break; - } + for (i = POLLFDS_BASE; i < n_pfds; i++) { + if (polls[i].fd == c->fd) { + revents = polls[i].revents; + break; } - pthread_mutex_unlock(&client_lock); - if (!c) { - condlog(4, "cli%d: new fd %d", - i, polls[i].fd); - continue; - } - get_monotonic_time(&start_time); - if (recv_packet_from_client(c->fd, &inbuf, - uxsock_timeout) - != 0) { - dead_client(c); - continue; - } - if (!inbuf) { - condlog(4, "recv_packet_from_client " - "get null request"); - continue; - } - condlog(4, "cli[%d]: Got request [%s]", - i, inbuf); - uxsock_trigger(inbuf, &reply, &rlen, - _socket_client_is_root(c->fd), - trigger_data); - if (reply) { - if (send_packet(c->fd, - reply) != 0) { - dead_client(c); - } else { - condlog(4, "cli[%d]: " - "Reply [%d bytes]", - i, rlen); - } - FREE(reply); - reply = NULL; - } - check_timeout(start_time, inbuf, - uxsock_timeout); - FREE(inbuf); + } + + handle_client(c, trigger_data, revents); + + if (c->error == -ECONNRESET) { + condlog(4, "cli[%d]: disconnected", c->fd); + dead_client(c); + if (i < n_pfds) + polls[i].fd = -1; } } /* see if we got a non-fatal signal */ handle_signals(true); /* see if we got a new client */ - if (polls[0].revents & POLLIN) { + if (polls[POLLFD_UX].revents & POLLIN) { new_client(ux_sock); } /* handle inotify events on config files */ - if (polls[1].revents & POLLIN) + if (polls[POLLFD_NOTIFY].revents & POLLIN) handle_inotify(notify_fd, &wds); } + pthread_cleanup_pop(1); return NULL; } diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h index 18f008d..60c3a2c 100644 --- a/multipathd/uxlsnr.h +++ b/multipathd/uxlsnr.h @@ -3,10 +3,8 @@ #include -typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *); - void uxsock_cleanup(void *arg); -void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, +void *uxsock_listen(long ux_sock, void * trigger_data); #endif diff --git a/multipathd/waiter.c b/multipathd/waiter.c index bbe6c2a..2a22146 100644 --- a/multipathd/waiter.c +++ b/multipathd/waiter.c @@ -13,7 +13,6 @@ #include "util.h" #include "vector.h" -#include "memory.h" #include "checkers.h" #include "config.h" #include "structs.h" @@ -32,8 +31,7 @@ static struct event_thread *alloc_waiter (void) struct event_thread *wp; - wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); - memset(wp, 0, sizeof(struct event_thread)); + wp = (struct event_thread *)calloc(1, sizeof(struct event_thread)); return wp; } @@ -46,7 +44,7 @@ static void free_waiter (void *data) dm_task_destroy(wp->dmt); rcu_unregister_thread(); - FREE(wp); + free(wp); } void stop_waiter_thread (struct multipath *mpp) diff --git a/tests/pgpolicy.c b/tests/pgpolicy.c index 57ad338..f116d12 100644 --- a/tests/pgpolicy.c +++ b/tests/pgpolicy.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "globals.c" #include "pgpolicies.h" diff --git a/tests/uevent.c b/tests/uevent.c index 648ff26..7523fec 100644 --- a/tests/uevent.c +++ b/tests/uevent.c @@ -194,7 +194,7 @@ static void test_dm_name_good(void **state) char *name = uevent_get_dm_name(uev); assert_string_equal(name, DM_NAME); - FREE(name); + free(name); } static void test_dm_name_bad_0(void **state) @@ -205,7 +205,7 @@ static void test_dm_name_bad_0(void **state) uev->envp[3] = "DM_NAME" DM_NAME; name = uevent_get_dm_name(uev); assert_ptr_equal(name, NULL); - FREE(name); + free(name); } static void test_dm_name_bad_1(void **state) @@ -216,7 +216,7 @@ static void test_dm_name_bad_1(void **state) uev->envp[3] = "DM_NAMES=" DM_NAME; name = uevent_get_dm_name(uev); assert_ptr_equal(name, NULL); - FREE(name); + free(name); } static void test_dm_name_good_1(void **state) @@ -228,7 +228,7 @@ static void test_dm_name_good_1(void **state) uev->envp[2] = "DM_NAME=" DM_NAME; name = uevent_get_dm_name(uev); assert_string_equal(name, DM_NAME); - FREE(name); + free(name); } static void test_dm_uuid_false_0(void **state) diff --git a/tests/vpd.c b/tests/vpd.c index 8e730d3..a7d2092 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -291,7 +292,9 @@ static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id, unsigned char *desc; int n = 0; - memset(buf, 0, bufsiz); + /* Fill with large number, which will cause length overflow */ + memset(buf, 0xed, bufsiz); + buf[0] = 0; buf[1] = 0x83; desc = buf + 4; @@ -500,6 +503,27 @@ static void test_vpd_naa_ ## naa ## _ ## wlen(void **state) \ test_id, vt->wwid); \ } +/** + * test_cpd_naa_NAA_badlen_BAD() - test detection of bad length fields + * @NAA: Network Name Authority (2, 3, 5, 16) + * @BAD: Value for designator length field + * @ERR: Expected error code + */ +#define make_test_vpd_naa_badlen(NAA, BAD, ERR) \ +static void test_vpd_naa_##NAA##_badlen_##BAD(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, 3, NAA, 0); \ + \ + vt->vpdbuf[7] = BAD; \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, 40); \ + assert_int_equal(-ret, -ERR); \ +} + /** * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64 * @LEN: EUI64 length (8, 12, or 16) @@ -532,6 +556,31 @@ static void test_vpd_eui_ ## len ## _ ## wlen ## _ ## sml(void **state) \ test_id, vt->wwid); \ } +/** + * test_cpd_eui_LEN_badlen_BAD() - test detection of bad length fields + * @NAA: correct length(8, 12, 16) + * @BAD: value for designator length field + * @ERR: expected error code + */ +#define make_test_vpd_eui_badlen(LEN, BAD, ERR) \ +static void test_vpd_eui_badlen_##LEN##_##BAD(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, 2, 0, LEN); \ + \ + vt->vpdbuf[7] = BAD; \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, 40); \ + assert_int_equal(ret, ERR); \ + if (ERR >= 0) \ + assert_correct_wwid("test_vpd_eui_badlen_"#LEN"_"#BAD, \ + 2 * BAD + 1, ret, '2', 0, true, \ + test_id, vt->wwid); \ +} + /** * test_vpd80_SIZE_LEN_WLEN() - test code for VPD 80 * @SIZE, @LEN: see create_vpd80() @@ -621,6 +670,17 @@ make_test_vpd_eui(8, 17, 0); make_test_vpd_eui(8, 16, 0); make_test_vpd_eui(8, 10, 0); +make_test_vpd_eui_badlen(8, 8, 17); +/* Invalid entry, length overflow */ +make_test_vpd_eui_badlen(8, 12, -EOVERFLOW); +make_test_vpd_eui_badlen(8, 9, -EOVERFLOW); +/* invalid entry, no length overflow, but no full next entry */ +make_test_vpd_eui_badlen(8, 7, -EINVAL); +make_test_vpd_eui_badlen(8, 5, -EINVAL); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_eui_badlen(8, 4, -EOVERFLOW); +make_test_vpd_eui_badlen(8, 0, -EOVERFLOW); + /* 96 bit, WWID size: 26 */ make_test_vpd_eui(12, 32, 0); make_test_vpd_eui(12, 26, 0); @@ -628,12 +688,38 @@ make_test_vpd_eui(12, 25, 0); make_test_vpd_eui(12, 20, 0); make_test_vpd_eui(12, 10, 0); +make_test_vpd_eui_badlen(12, 12, 25); +make_test_vpd_eui_badlen(12, 16, -EOVERFLOW); +make_test_vpd_eui_badlen(12, 13, -EOVERFLOW); +/* invalid entry, no length overflow, but no full next entry */ +make_test_vpd_eui_badlen(12, 11, -EINVAL); +make_test_vpd_eui_badlen(12, 9, -EINVAL); +/* non-fatal - valid 8-byte descriptor */ +make_test_vpd_eui_badlen(12, 8, 17); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_eui_badlen(12, 7, -EOVERFLOW); +make_test_vpd_eui_badlen(12, 0, -EOVERFLOW); + /* 128 bit, WWID size: 34 */ make_test_vpd_eui(16, 40, 0); make_test_vpd_eui(16, 34, 0); make_test_vpd_eui(16, 33, 0); make_test_vpd_eui(16, 20, 0); +make_test_vpd_eui_badlen(16, 16, 33); +make_test_vpd_eui_badlen(16, 17, -EOVERFLOW); +make_test_vpd_eui_badlen(16, 15, -EINVAL); +make_test_vpd_eui_badlen(16, 13, -EINVAL); +/* non-fatal - valid 12-byte descriptor */ +make_test_vpd_eui_badlen(16, 12, 25); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_eui_badlen(16, 11, -EOVERFLOW); +/* non-fatal - valid 8-byte descriptor */ +make_test_vpd_eui_badlen(16, 8, 17); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_eui_badlen(16, 7, -EOVERFLOW); +make_test_vpd_eui_badlen(16, 0, -EOVERFLOW); + /* NAA IEEE registered extended (36), WWID size: 34 */ make_test_vpd_naa(6, 40); make_test_vpd_naa(6, 34); @@ -641,12 +727,33 @@ make_test_vpd_naa(6, 33); make_test_vpd_naa(6, 32); make_test_vpd_naa(6, 20); +/* NAA IEEE registered extended with bad designator length */ +make_test_vpd_naa_badlen(6, 16, 33); +/* offset overflow */ +make_test_vpd_naa_badlen(6, 17, -EOVERFLOW); +/* invalid entry, no length overflow, but no full next entry */ +make_test_vpd_naa_badlen(6, 15, -EINVAL); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_naa_badlen(6, 8, -EOVERFLOW); +make_test_vpd_naa_badlen(6, 0, -EOVERFLOW); + /* NAA IEEE registered (35), WWID size: 18 */ make_test_vpd_naa(5, 20); make_test_vpd_naa(5, 18); make_test_vpd_naa(5, 17); make_test_vpd_naa(5, 16); +/* NAA IEEE registered with bad designator length */ +make_test_vpd_naa_badlen(5, 8, 17); +/* offset overflow */ +make_test_vpd_naa_badlen(5, 16, -EOVERFLOW); +make_test_vpd_naa_badlen(5, 9, -EOVERFLOW); +/* invalid entry, no length overflow, but no full next entry */ +make_test_vpd_naa_badlen(5, 7, -EINVAL); +/* invalid entry, length of next one readable but too long */ +make_test_vpd_naa_badlen(5, 4, -EOVERFLOW); +make_test_vpd_naa_badlen(5, 0, -EOVERFLOW); + /* NAA local (33), WWID size: 18 */ make_test_vpd_naa(3, 20); make_test_vpd_naa(3, 18); @@ -741,24 +848,59 @@ static int test_vpd(void) cmocka_unit_test(test_vpd_eui_8_17_0), cmocka_unit_test(test_vpd_eui_8_16_0), cmocka_unit_test(test_vpd_eui_8_10_0), + cmocka_unit_test(test_vpd_eui_badlen_8_8), + cmocka_unit_test(test_vpd_eui_badlen_8_12), + cmocka_unit_test(test_vpd_eui_badlen_8_9), + cmocka_unit_test(test_vpd_eui_badlen_8_7), + cmocka_unit_test(test_vpd_eui_badlen_8_5), + cmocka_unit_test(test_vpd_eui_badlen_8_4), + cmocka_unit_test(test_vpd_eui_badlen_8_0), cmocka_unit_test(test_vpd_eui_12_32_0), cmocka_unit_test(test_vpd_eui_12_26_0), cmocka_unit_test(test_vpd_eui_12_25_0), cmocka_unit_test(test_vpd_eui_12_20_0), cmocka_unit_test(test_vpd_eui_12_10_0), + cmocka_unit_test(test_vpd_eui_badlen_12_12), + cmocka_unit_test(test_vpd_eui_badlen_12_16), + cmocka_unit_test(test_vpd_eui_badlen_12_13), + cmocka_unit_test(test_vpd_eui_badlen_12_11), + cmocka_unit_test(test_vpd_eui_badlen_12_9), + cmocka_unit_test(test_vpd_eui_badlen_12_8), + cmocka_unit_test(test_vpd_eui_badlen_12_7), + cmocka_unit_test(test_vpd_eui_badlen_12_0), cmocka_unit_test(test_vpd_eui_16_40_0), cmocka_unit_test(test_vpd_eui_16_34_0), cmocka_unit_test(test_vpd_eui_16_33_0), cmocka_unit_test(test_vpd_eui_16_20_0), + cmocka_unit_test(test_vpd_eui_badlen_16_16), + cmocka_unit_test(test_vpd_eui_badlen_16_17), + cmocka_unit_test(test_vpd_eui_badlen_16_15), + cmocka_unit_test(test_vpd_eui_badlen_16_13), + cmocka_unit_test(test_vpd_eui_badlen_16_12), + cmocka_unit_test(test_vpd_eui_badlen_16_11), + cmocka_unit_test(test_vpd_eui_badlen_16_8), + cmocka_unit_test(test_vpd_eui_badlen_16_7), + cmocka_unit_test(test_vpd_eui_badlen_16_0), cmocka_unit_test(test_vpd_naa_6_40), cmocka_unit_test(test_vpd_naa_6_34), cmocka_unit_test(test_vpd_naa_6_33), cmocka_unit_test(test_vpd_naa_6_32), cmocka_unit_test(test_vpd_naa_6_20), + cmocka_unit_test(test_vpd_naa_6_badlen_16), + cmocka_unit_test(test_vpd_naa_6_badlen_15), + cmocka_unit_test(test_vpd_naa_6_badlen_8), + cmocka_unit_test(test_vpd_naa_6_badlen_17), + cmocka_unit_test(test_vpd_naa_6_badlen_0), cmocka_unit_test(test_vpd_naa_5_20), cmocka_unit_test(test_vpd_naa_5_18), cmocka_unit_test(test_vpd_naa_5_17), cmocka_unit_test(test_vpd_naa_5_16), + cmocka_unit_test(test_vpd_naa_5_badlen_8), + cmocka_unit_test(test_vpd_naa_5_badlen_7), + cmocka_unit_test(test_vpd_naa_5_badlen_4), + cmocka_unit_test(test_vpd_naa_5_badlen_16), + cmocka_unit_test(test_vpd_naa_5_badlen_9), + cmocka_unit_test(test_vpd_naa_5_badlen_0), cmocka_unit_test(test_vpd_naa_3_20), cmocka_unit_test(test_vpd_naa_3_18), cmocka_unit_test(test_vpd_naa_3_17), -- 2.34.1