Imported Upstream version 0.8.7 upstream/0.8.7
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:20 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:20 +0000 (13:50 +0900)
86 files changed:
.gitignore
Makefile.inc
README.alua
kpartx/kpartx.c
libdmmp/Makefile
libdmmp/docs/man/dmmp_context_free.3
libdmmp/docs/man/dmmp_context_log_func_set.3
libdmmp/docs/man/dmmp_context_log_priority_get.3
libdmmp/docs/man/dmmp_context_log_priority_set.3
libdmmp/docs/man/dmmp_context_new.3
libdmmp/docs/man/dmmp_context_timeout_get.3
libdmmp/docs/man/dmmp_context_timeout_set.3
libdmmp/docs/man/dmmp_context_userdata_get.3
libdmmp/docs/man/dmmp_context_userdata_set.3
libdmmp/docs/man/dmmp_flush_mpath.3
libdmmp/docs/man/dmmp_last_error_msg.3
libdmmp/docs/man/dmmp_log_priority_str.3
libdmmp/docs/man/dmmp_mpath_array_free.3
libdmmp/docs/man/dmmp_mpath_array_get.3
libdmmp/docs/man/dmmp_mpath_kdev_name_get.3
libdmmp/docs/man/dmmp_mpath_name_get.3
libdmmp/docs/man/dmmp_mpath_wwid_get.3
libdmmp/docs/man/dmmp_path_array_get.3
libdmmp/docs/man/dmmp_path_blk_name_get.3
libdmmp/docs/man/dmmp_path_group_array_get.3
libdmmp/docs/man/dmmp_path_group_id_get.3
libdmmp/docs/man/dmmp_path_group_priority_get.3
libdmmp/docs/man/dmmp_path_group_selector_get.3
libdmmp/docs/man/dmmp_path_group_status_get.3
libdmmp/docs/man/dmmp_path_group_status_str.3
libdmmp/docs/man/dmmp_path_status_get.3
libdmmp/docs/man/dmmp_path_status_str.3
libdmmp/docs/man/dmmp_reconfig.3
libdmmp/docs/man/dmmp_strerror.3
libmpathpersist/mpath_persist.c
libmultipath/Makefile
libmultipath/alias.c
libmultipath/blacklist.c
libmultipath/checkers.c
libmultipath/configure.c
libmultipath/configure.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/dict.h
libmultipath/discovery.c
libmultipath/dmparser.c
libmultipath/dmparser.h
libmultipath/foreign.c
libmultipath/foreign.h
libmultipath/foreign/nvme.c
libmultipath/generic.c
libmultipath/generic.h
libmultipath/hwtable.c
libmultipath/libmultipath.version
libmultipath/parser.c
libmultipath/parser.h
libmultipath/print.c
libmultipath/print.h
libmultipath/prioritizers/weightedpath.c
libmultipath/propsel.c
libmultipath/strbuf.c [new file with mode: 0644]
libmultipath/strbuf.h [new file with mode: 0644]
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/structs_vec.h
libmultipath/sysfs.c
libmultipath/uevent.c
libmultipath/util.c
libmultipath/util.h
libmultipath/version.h
multipath/main.c
multipath/multipath.8
multipath/multipath.conf.5
multipathd/Makefile
multipathd/cli.c
multipathd/cli_handlers.c
multipathd/dmevents.c
multipathd/main.c
multipathd/main.h
multipathd/multipathd.service
tests/Makefile
tests/alias.c
tests/dmevents.c
tests/strbuf.c [new file with mode: 0644]

index 9926756b8b936838bfe399989be69fae0e159567..087dffc216c09a2209198d47c6eeb3ca9619e286 100644 (file)
@@ -6,6 +6,9 @@
 *.a
 *.gz
 *.d
+\#*
+cscope.files
+cscope.out
 kpartx/kpartx
 multipath/multipath
 multipathd/multipathd
@@ -21,5 +24,6 @@ libdmmp/test/libdmmp_test
 libdmmp/test/libdmmp_speed_test
 tests/*-test
 tests/*.out
+tests/*.vgr
 libmultipath/nvme-ioctl.c
 libmultipath/nvme-ioctl.h
index f1e231319fe359ec2abd66689d74b661009b692f..d0ec9b441e8ad230783210b3874022ae86ddabb7 100644 (file)
@@ -95,12 +95,13 @@ TEST_CC_OPTION = $(shell \
 STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
 ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
 WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
+WFORMATOVERFLOW := $(call TEST_CC_OPTION,-Wformat-overflow=2,)
 
 OPTFLAGS       := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
-WARNFLAGS      := -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
+WARNFLAGS      := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
                  -Werror=implicit-function-declaration -Werror=format-security \
                  $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
-CPPFLAGS       := -Wp,-D_FORTIFY_SOURCE=2 
+CPPFLAGS       := -Wp,-D_FORTIFY_SOURCE=2
 CFLAGS         := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
                   -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
                   -MMD -MP
index b15eb4876fdf9dea5db34de5b6478df552988ce5..5d2b1c6442066bb0fe0a3c2a9ad242e190d906dc 100644 (file)
@@ -6,7 +6,7 @@ To enable ALUA, the following options should be changed:
 - EMC CLARiiON/VNX:
    "Failover Mode" should be changed to "4" or "Active-Active mode(ALUA)-failover mode 4"
 
-- HPE 3PAR:
+- HPE 3PAR, Primera, and Alletra 9000:
    "Host:" should be changed to "Generic-ALUA Persona 2 (UARepLun, SESLun, ALUA)".
 
 - Promise VTrak/Vess:
index 8ff116b82bfe81bf054163727decccae49b0ada3..7bc645438016ace2a58808769b3c687e2b22d0ce 100644 (file)
@@ -766,6 +766,8 @@ getblock (int fd, unsigned int blknr) {
        if (read(fd, bp->block, secsz) != secsz) {
                fprintf(stderr, "read error, sector %d\n", secnr);
                blockhead = bp->next;
+               free(bp->block);
+               free(bp);
                return NULL;
        }
 
index 764a0bc5476e372afedce90a360df623e7151cfc..79b92fb2edbddab24506dbb0a154437ac41ba91c 100644 (file)
@@ -76,6 +76,8 @@ docs/man/%.3.gz:      docs/man/%.3
 docs/man/dmmp_strerror.3:      $(HEADERS)
        TEMPFILE=$(shell mktemp); \
        cat $^ | perl docs/doc-preclean.pl >$$TEMPFILE; \
+       LC_ALL=C \
+       KBUILD_BUILD_TIMESTAMP=`git log -n1 --pretty=%cd --date=iso -- $^` \
        perl docs/kernel-doc -man $$TEMPFILE | \
            perl docs/split-man.pl docs/man; \
        rm -f $$TEMPFILE
index 0d26f42c25533c363e84f62177084aeef2805b28..7c109e13e336acddfd30cc5c5475a67af549cd16 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_free" 3 "dmmp_context_free" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_free" 3 "dmmp_context_free" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_free \- Release the memory of struct dmmp_context.
 .SH SYNOPSIS
index 986793db05215403f41c6d66ef9e2032fa30f262..be311ecf17cb5e5d99bc04d63adb1db5316a08ea 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_log_func_set \- Set log handler function.
 .SH SYNOPSIS
index 9a273a28596ddcbfe346b5b9df70f9e13425ea2b..be383013087a2f76b969bd96fcd1af9eb88f838f 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_log_priority_get \- Get log priority.
 .SH SYNOPSIS
index 469c5a49ed383f7d1f17b7209812d63ecbd193e2..79e4d2e800445acb9f2948555b0e91e69934b822 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_log_priority_set \- Set log priority.
 .SH SYNOPSIS
index 0eaeb00d715f442a1e46cefd2f681b14728b6109..12505f9159e03f61177666c38f1eaebddb4d6c6e 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_new" 3 "dmmp_context_new" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_new" 3 "dmmp_context_new" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_new \- Create struct dmmp_context.
 .SH SYNOPSIS
index 1df27936ece6bdb71be47031ea1eed44d44ee879..2ed825d56ad6db8e68388767429c3983f55fc703 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_timeout_get \- Get IPC timeout.
 .SH SYNOPSIS
index f3d7709286268f577f30b73d905b02dfe0c6009c..16bc9d998f33b8444322875238e7ca13cf954ce9 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_timeout_set \- Set IPC timeout.
 .SH SYNOPSIS
index fb713d505c0456c0ec9ed8526a6f43c31d415932..eff446c63fe657f2a5165c4436ad2a9cfe666375 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_userdata_get \- Get user data pointer.
 .SH SYNOPSIS
index c5bf63f30ca74f35c22d81a3549121055a87f882..d7be869f3851e4b757fd87c7cd302a0e80afcbda 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_context_userdata_set \- Set user data pointer.
 .SH SYNOPSIS
index cdfd52664d2db22a747b870854c83a0b869787b7..359607ed6772bf98f38c249c8d97f1d82d2b08d5 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_flush_mpath \- Flush specified multipath device map if unused.
 .SH SYNOPSIS
index 20acbc6ac8cbd772df1ab961369153287aecfa14..378c55a52577ea2d524eb18d8a824cc0ca9b260c 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_last_error_msg \- Retrieves the last error message.
 .SH SYNOPSIS
index 3b5f8284e62a4ef65bc88369e9651d6a052370bc..b2761602fcd4b1400cea6cbe9e5c04a160233b24 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_log_priority_str \- Convert log priority to string.
 .SH SYNOPSIS
index 8c294e0dfc784772dfc36f0c9b1b4392d0a9c127..0514a66f15e53392999d501c6a8900ffe88feb48 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_mpath_array_free \- Free 'struct dmmp_mpath' pointer array.
 .SH SYNOPSIS
index e211db424b4c456e39625c48f4e33bb398c2bedf..8b0e5b53dbdbd30e8ca73944ad37a08a6a6e9256 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_mpath_array_get \- Query all existing multipath devices.
 .SH SYNOPSIS
index e802fe6d3deb0328dd475e8fa20921f564d9d22e..ddead551e9ca21e48ac8cf1e0bf1ea18436851ec 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_mpath_kdev_name_get \- Retrieve kernel DEVNAME of certain mpath.
 .SH SYNOPSIS
index d70579e5ef7981c4e6dda35364f1057d1e9a9f37..2b0027e54591c72ae9740262e379392cd2e1c7ec 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_mpath_name_get \- Retrieve name(alias) of certain mpath.
 .SH SYNOPSIS
index 3d060e92181a087e85cd30e70150e01c1e151ee4..b8e9e7d8f237a5b12d3901403905687d71eb29b4 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_mpath_wwid_get \- Retrieve WWID of certain mpath.
 .SH SYNOPSIS
index 53340b3da97089ddfbde8045f26d2455e450f8ce..21f486bea993460cca8a0d805ab8aea82fbd3c5e 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_array_get \- Retrieve path pointer array.
 .SH SYNOPSIS
index da5f9f03488c93ddf8501f4c0f6e40b32ff18e34..5938f0e72cd82ae827eb84ff178c1fef48127530 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_blk_name_get \- Retrieve block name.
 .SH SYNOPSIS
index 6eee4a2b800e59268ae45839d7c73fa9d10e169c..ca3187cbbb5021f123c75adae1f0a076cd9ff44e 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_array_get \- Retrieve path groups pointer array.
 .SH SYNOPSIS
index 4f07b5367923df0a84e6412300f1907969af2ba0..a84f31f0ac3060b0962437d19db7b313be1ef8ea 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_id_get \- Retrieve path group ID.
 .SH SYNOPSIS
index a48b2704a8f6b3a4bca59557585380a93ac0665c..1cda8af359432c67521abc6b74e887106536e7a9 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_priority_get \- Retrieve path group priority.
 .SH SYNOPSIS
index 407b3f415ddc83b9a6afa791a536b921b4f8d9ad..f55477bbaa233bb41cc38f833ea7282083cc065d 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_selector_get \- Retrieve path group selector.
 .SH SYNOPSIS
index a81aeb3afcce589a62861df61e98a5ef048bac80..53e68b8ea06d78e3f1721995116346b1c1785862 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_status_get \- Retrieve path group status.
 .SH SYNOPSIS
index e4a9f74bba8c8694f61e800cda5ec7100c9f4793..98f877a49e63847df29f710e578d3efd1d280470 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_group_status_str \- Convert path group status to string.
 .SH SYNOPSIS
index 025cfee5d4ee13611846da4ec804da05f66f809c..baa4437d5a2236711a5e2ed6384600f911af5600 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_status_get \- Retrieve the path status.
 .SH SYNOPSIS
index 3944d3999b9adbe3d35fdeefca7bf0f3d0cc48b9..425e472aa88f84ca51e0c14aa7cbe0d6bbbd6588 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_path_status_str \- Convert path status to string.
 .SH SYNOPSIS
index a743e3083de6fe1254217e377b0a8027e0d36d02..36bd5041ace947918560fd378fc27949857d1f38 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_reconfig" 3 "dmmp_reconfig" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_reconfig" 3 "dmmp_reconfig" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_reconfig \- Instruct multipathd daemon to do reconfiguration.
 .SH SYNOPSIS
index 4d753d36ce29d1c076fd375da2089b3a2a70c9ca..3acd9c9d3669a33c3c357c6c38c38feb0c02e6f5 100644 (file)
@@ -1,4 +1,4 @@
-.TH "dmmp_strerror" 3 "dmmp_strerror" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.TH "dmmp_strerror" 3 "dmmp_strerror" "March 2018" "Device Mapper Multipath API - libdmmp Manual" 
 .SH NAME
 dmmp_strerror \- Convert error code to string.
 .SH SYNOPSIS
index 190e9707c54da7c0729f0bf2b7452dd321836a6c..803a2a284874c85a633f9114000b7d7f875367dd 100644 (file)
@@ -410,7 +410,7 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
                if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
                    update_mpp_paths(mpp, pathvec)) {
                        condlog(1, "error parsing map %s", mpp->wwid);
-                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
+                       remove_map(mpp, pathvec, curmp);
                        i--;
                } else
                        extract_hwe_from_path(mpp);
@@ -604,7 +604,8 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
                        return ret ;
                }
        }
-       return MPATH_PR_SUCCESS;
+       condlog (0, "%s: no path available", mpp->wwid);
+       return MPATH_PR_DMMP_ERROR;
 }
 
 int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
@@ -663,6 +664,11 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
 
        active_pathcount = count_active_paths(mpp);
 
+       if (active_pathcount == 0) {
+               condlog (0, "%s: no path available", mpp->wwid);
+               return MPATH_PR_DMMP_ERROR;
+       }
+
        struct threadinfo thread[active_pathcount];
        memset(thread, 0, sizeof(thread));
        for (i = 0; i < active_pathcount; i++){
index e7254f393a7f578b6a8e3177b6b3f011b68fab69..7f3921c517f6e9134a738fce96746e7411dc798f 100644 (file)
@@ -53,7 +53,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
        lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
        io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
-       libsg.o valid.o
+       libsg.o valid.o strbuf.o
 
 all:   $(DEVLIB)
 
index 02bc9d65568942765124d47453357ddcb4633600..ad7e512ba143ad8d97e394db4bfdd224a1eab805 100644 (file)
@@ -22,7 +22,7 @@
 #include "util.h"
 #include "errno.h"
 #include "devmapper.h"
-
+#include "strbuf.h"
 
 /*
  * significant parts of this file were taken from iscsi-bindings.c of the
@@ -61,31 +61,23 @@ valid_alias(const char *alias)
        return 1;
 }
 
-
-static int
-format_devname(char *name, int id, int len, const char *prefix)
+static int format_devname(struct strbuf *buf, int id)
 {
-       int pos;
-       int prefix_len = strlen(prefix);
+       /*
+        * We need: 7 chars for 32bit integers, 14 chars for 64bit integers
+        */
+       char devname[2 * sizeof(int)];
+       int pos = sizeof(devname) - 1, rc;
 
-       if (len <= prefix_len + 1 || id <= 0)
+       if (id <= 0)
                return -1;
 
-       memset(name, 0, len);
-       strcpy(name, prefix);
-       name[len - 1] = '\0';
-       for (pos = len - 2; pos >= prefix_len; pos--) {
-               id--;
-               name[pos] = 'a' + id % 26;
-               if (id < 26)
-                       break;
-               id /= 26;
-       }
-       if (pos < prefix_len)
-               return -1;
+       devname[pos] = '\0';
+       for (; id >= 1; id /= 26)
+               devname[--pos] = 'a' + --id % 26;
 
-       memmove(name + prefix_len, name + pos, len - pos);
-       return (prefix_len + len - pos - 1);
+       rc = append_strbuf_str(buf, devname + pos);
+       return rc >= 0 ? rc : -1;
 }
 
 static int
@@ -123,11 +115,14 @@ scan_devname(const char *alias, const char *prefix)
 static int
 id_already_taken(int id, const char *prefix, const char *map_wwid)
 {
-       char alias[LINE_MAX];
+       STRBUF_ON_STACK(buf);
+       const char *alias;
 
-       if (format_devname(alias, id, LINE_MAX, prefix) < 0)
+       if (append_strbuf_str(&buf, prefix) < 0 ||
+           format_devname(&buf, id) < 0)
                return 0;
 
+       alias = get_strbuf_str(&buf);
        if (dm_map_present(alias)) {
                char wwid[WWID_SIZE];
 
@@ -285,10 +280,10 @@ rlookup_binding(FILE *f, char *buff, const char *map_alias)
 static char *
 allocate_binding(int fd, const char *wwid, int id, const char *prefix)
 {
-       char buf[LINE_MAX];
+       STRBUF_ON_STACK(buf);
        off_t offset;
+       ssize_t len;
        char *alias, *c;
-       int i;
 
        if (id <= 0) {
                condlog(0, "%s: cannot allocate new binding for id %d",
@@ -296,16 +291,12 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
                return NULL;
        }
 
-       i = format_devname(buf, id, LINE_MAX, prefix);
-       if (i == -1)
+       if (append_strbuf_str(&buf, prefix) < 0 ||
+           format_devname(&buf, id) == -1)
                return NULL;
 
-       c = buf + i;
-       if (snprintf(c, LINE_MAX - i, " %s\n", wwid) >= LINE_MAX - i) {
-               condlog(1, "%s: line too long for %s\n", __func__, wwid);
+       if (print_strbuf(&buf, " %s\n", wwid) < 0)
                return NULL;
-       }
-       buf[LINE_MAX - 1] = '\0';
 
        offset = lseek(fd, 0, SEEK_END);
        if (offset < 0){
@@ -313,24 +304,25 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
                        strerror(errno));
                return NULL;
        }
-       if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)){
+
+       len = get_strbuf_len(&buf);
+       alias = steal_strbuf_str(&buf);
+
+       if (write(fd, alias, len) != len) {
                condlog(0, "Cannot write binding to bindings file : %s",
                        strerror(errno));
                /* clear partial write */
                if (ftruncate(fd, offset))
                        condlog(0, "Cannot truncate the header : %s",
                                strerror(errno));
+               free(alias);
                return NULL;
        }
-       c = strchr(buf, ' ');
+       c = strchr(alias, ' ');
        if (c)
                *c = '\0';
 
-       condlog(3, "Created new binding [%s] for WWID [%s]", buf, wwid);
-       alias = strdup(buf);
-       if (alias == NULL)
-               condlog(0, "cannot copy new alias from bindings file: out of memory");
-
+       condlog(3, "Created new binding [%s] for WWID [%s]", alias, wwid);
        return alias;
 }
 
@@ -560,7 +552,7 @@ static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
 static int write_bindings_file(const Bindings *bindings, int fd)
 {
        struct binding *bnd;
-       char line[LINE_MAX];
+       STRBUF_ON_STACK(line);
        int i;
 
        if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
@@ -570,16 +562,12 @@ static int write_bindings_file(const Bindings *bindings, int fd)
        vector_foreach_slot(bindings, bnd, i) {
                int len;
 
-               len = snprintf(line, sizeof(line), "%s %s\n",
-                              bnd->alias, bnd->wwid);
-
-               if (len < 0 || (size_t)len >= sizeof(line)) {
-                       condlog(1, "%s: line overflow", __func__);
+               if ((len = print_strbuf(&line, "%s %s\n",
+                                       bnd->alias, bnd->wwid)) < 0)
                        return -1;
-               }
-
-               if (write(fd, line, len) != len)
+               if (write(fd, get_strbuf_str(&line), len) != len)
                        return -1;
+               truncate_strbuf(&line, 0);
        }
        return 0;
 }
index 6c6a597948343eb1e72689399d16b8e3a448c3eb..4e315c9732eef427b092d537cd58378a05ea8646 100644 (file)
@@ -14,6 +14,7 @@
 #include "blacklist.h"
 #include "structs_vec.h"
 #include "print.h"
+#include "strbuf.h"
 
 char *check_invert(char *str, bool *invert)
 {
@@ -341,19 +342,21 @@ int
 filter_protocol(const struct _vector *blist, const struct _vector *elist,
                const struct path *pp)
 {
-       char buf[PROTOCOL_BUF_SIZE];
+       STRBUF_ON_STACK(buf);
+       const char *prot;
        int r = MATCH_NOTHING;
 
        if (pp) {
-               snprint_path_protocol(buf, sizeof(buf), pp);
+               snprint_path_protocol(&buf, pp);
+               prot = get_strbuf_str(&buf);
 
-               if (match_reglist(elist, buf))
+               if (match_reglist(elist, prot))
                        r = MATCH_PROTOCOL_BLIST_EXCEPT;
-               else if (match_reglist(blist, buf))
+               else if (match_reglist(blist, prot))
                        r = MATCH_PROTOCOL_BLIST;
+               log_filter(pp->dev, NULL, NULL, NULL, NULL, prot, r, 3);
        }
 
-       log_filter(pp->dev, NULL, NULL, NULL, NULL, buf, r, 3);
        return r;
 }
 
index 2dd9915dcbf247cbe13c441b9aec4f3391001385..8039c2bf1fa9193c2998d756baf2d42eab540487 100644 (file)
@@ -368,7 +368,7 @@ static void checker_cleanup_thread(void *arg)
 {
        struct checker_class *cls = arg;
 
-       (void)checker_class_unref(cls);
+       free_checker_class(cls);
        rcu_unregister_thread();
 }
 
index 6ca1f4bb4e28921ba2eda4e750c6237b1bf00622..7edb355b63e5d0af2da7aa21b4d356331ffdb87f 100644 (file)
@@ -292,8 +292,7 @@ static int wait_for_pending_paths(struct multipath *mpp,
        return n_pending;
 }
 
-int setup_map(struct multipath *mpp, char *params, int params_size,
-             struct vectors *vecs)
+int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
 {
        struct pathgroup * pgp;
        struct config *conf;
@@ -397,7 +396,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
                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
         */
@@ -462,7 +461,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
         * transform the mp->pg vector of vectors of paths
         * into a mp->params strings to feed the device-mapper
         */
-       if (assemble_map(mpp, params, params_size)) {
+       if (assemble_map(mpp, params)) {
                condlog(0, "%s: problem assembing map", mpp->alias);
                return 1;
        }
@@ -811,7 +810,7 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                remove_feature(&mpp_feat, "retain_attached_hw_handler");
                remove_feature(&cmpp_feat, "queue_if_no_path");
                remove_feature(&cmpp_feat, "retain_attached_hw_handler");
-               if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) {
+               if (strcmp(mpp_feat, cmpp_feat)) {
                        select_reload_action(mpp, "features change");
                        FREE(cmpp_feat);
                        FREE(mpp_feat);
@@ -1061,29 +1060,8 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
        return DOMAP_FAIL;
 }
 
-static int
-deadmap (struct multipath * mpp)
-{
-       int i, j;
-       struct pathgroup * pgp;
-       struct path * pp;
-
-       if (!mpp->pg)
-               return 1;
-
-       vector_foreach_slot (mpp->pg, pgp, i) {
-               if (!pgp->paths)
-                       continue;
-
-               vector_foreach_slot (pgp->paths, pp, j)
-                       if (strlen(pp->dev))
-                               return 0; /* alive */
-       }
-
-       return 1; /* dead */
-}
-
-int check_daemon(void)
+extern int
+check_daemon(void)
 {
        int fd;
        char *reply;
@@ -1128,14 +1106,14 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
        int ret = CP_FAIL;
        int k, i, r;
        int is_daemon = (cmd == CMD_NONE) ? 1 : 0;
-       char params[PARAMS_SIZE];
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
        struct multipath * mpp;
-       struct path * pp1;
+       struct path * pp1 = NULL;
        struct path * pp2;
        vector curmp = vecs->mpvec;
        vector pathvec = vecs->pathvec;
        vector newmp;
-       struct config *conf;
+       struct config *conf = NULL;
        int allow_queueing;
        struct bitfield *size_mismatch_seen;
 
@@ -1220,7 +1198,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
                if (!mpp->paths) {
                        condlog(0, "%s: skip coalesce (no paths)", mpp->alias);
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                       remove_map(mpp, vecs->pathvec, NULL);
                        continue;
                }
 
@@ -1247,9 +1225,8 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
                }
                verify_paths(mpp);
 
-               params[0] = '\0';
-               if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+               if (setup_map(mpp, &params, vecs)) {
+                       remove_map(mpp, vecs->pathvec, NULL);
                        continue;
                }
 
@@ -1260,6 +1237,8 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
                                      force_reload == FORCE_RELOAD_YES ? 1 : 0);
 
                r = domap(mpp, params, is_daemon);
+               free(params);
+               params = NULL;
 
                if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
                        condlog(3, "%s: domap (%u) failure "
@@ -1269,7 +1248,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
                                condlog(2, "%s: %s map",
                                        mpp->alias, (mpp->action == ACT_CREATE)?
                                        "ignoring" : "removing");
-                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                               remove_map(mpp, vecs->pathvec, NULL);
                                continue;
                        } else /* if (r == DOMAP_RETRY && !is_daemon) */ {
                                ret = CP_RETRY;
@@ -1278,7 +1257,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
                }
                if (r == DOMAP_DRY) {
                        if (!vector_alloc_slot(newmp)) {
-                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                               remove_map(mpp, vecs->pathvec, NULL);
                                goto out;
                        }
                        vector_set_slot(newmp, mpp);
@@ -1303,38 +1282,13 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
                if (mpp->action != ACT_REJECT) {
                        if (!vector_alloc_slot(newmp)) {
-                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                               remove_map(mpp, vecs->pathvec, NULL);
                                goto out;
                        }
                        vector_set_slot(newmp, mpp);
                }
                else
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec,
-                                  KEEP_VEC);
-       }
-       /*
-        * Flush maps with only dead paths (ie not in sysfs)
-        * Keep maps with only failed paths
-        */
-       if (mpvec) {
-               vector_foreach_slot (newmp, mpp, i) {
-                       char alias[WWID_SIZE];
-
-                       if (!deadmap(mpp))
-                               continue;
-
-                       strlcpy(alias, mpp->alias, WWID_SIZE);
-
-                       vector_del_slot(newmp, i);
-                       i--;
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
-
-                       if (dm_flush_map(alias))
-                               condlog(2, "%s: remove failed (dead)",
-                                       alias);
-                       else
-                               condlog(2, "%s: remove (dead)", alias);
-               }
+                       remove_map(mpp, vecs->pathvec, NULL);
        }
        ret = CP_OK;
 out:
index 70cf77a367deb95b74b339ee1d507f5f2279cbc7..efe18b7d449673cdaebb7818cbdd1c5def38590e 100644 (file)
@@ -47,8 +47,7 @@ enum {
 
 struct vectors;
 
-int setup_map (struct multipath * mpp, char * params, int params_size,
-              struct vectors *vecs );
+int setup_map (struct multipath * mpp, char **params, struct vectors *vecs);
 void select_action (struct multipath *mpp, const struct _vector *curmp,
                    int force_reload);
 int domap (struct multipath * mpp, char * params, int is_daemon);
@@ -60,3 +59,4 @@ struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
 void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
 void trigger_partitions_udev_change(struct udev_device *dev, const char *action,
                                    int len);
+int check_daemon(void);
index 095cbc0c17a30b0260f376303f74860857491492..c05dc201e5a086665819c12cdb97d03c53cfeed4 100644 (file)
@@ -49,6 +49,9 @@ static int dm_conf_verbosity;
 
 #ifdef LIBDM_API_DEFERRED
 static int dm_cancel_remove_partmaps(const char * mapname);
+#define __DR_UNUSED__ /* empty */
+#else
+#define __DR_UNUSED__ __attribute__((unused))
 #endif
 
 static int do_foreach_partmaps(const char * mapname,
@@ -384,7 +387,8 @@ libmp_dm_task_create(int task)
 #define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS)
 
 static int
-dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) {
+dm_simplecmd (int task, const char *name, int no_flush, int need_sync,
+             uint16_t udev_flags, int deferred_remove __DR_UNUSED__) {
        int r = 0;
        int udev_wait_flag = ((need_sync || udev_flags) &&
                              (task == DM_DEVICE_RESUME ||
@@ -598,8 +602,8 @@ int dm_addmap_reload(struct multipath *mpp, char *params, int flush)
                return r;
 
        /* If the resume failed, dm will leave the device suspended, and
-        * drop the new table, so doing a second resume will try using
-        * the original table */
+        * drop the new table, so doing a second resume will try using
+        * the original table */
        if (dm_is_suspended(mpp->alias))
                dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, !flush, 1,
                             udev_flags, 0);
@@ -644,7 +648,7 @@ int dm_map_present(const char * str)
        return (do_get_info(str, &info) == 0);
 }
 
-int dm_get_map(const char *name, unsigned long long *size, char *outparams)
+int dm_get_map(const char *name, unsigned long long *size, char **outparams)
 {
        int r = DMP_ERR;
        struct dm_task *dmt;
@@ -678,12 +682,13 @@ int dm_get_map(const char *name, unsigned long long *size, char *outparams)
        if (size)
                *size = length;
 
-       if (!outparams) {
+       if (!outparams)
                r = DMP_OK;
-               goto out;
+       else {
+               *outparams = strdup(params);
+               r = *outparams ? DMP_OK : DMP_ERR;
        }
-       if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
-               r = DMP_OK;
+
 out:
        dm_task_destroy(dmt);
        return r;
@@ -757,7 +762,7 @@ is_mpath_part(const char *part_name, const char *map_name)
        return 0;
 }
 
-int dm_get_status(const char *name, char *outstatus)
+int dm_get_status(const char *name, char **outstatus)
 {
        int r = DMP_ERR;
        struct dm_task *dmt;
@@ -795,8 +800,12 @@ int dm_get_status(const char *name, char *outstatus)
                goto out;
        }
 
-       if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
+       if (!outstatus)
                r = DMP_OK;
+       else {
+               *outstatus = strdup(status);
+               r = *outstatus ? DMP_OK : DMP_ERR;
+       }
 out:
        if (r != DMP_OK)
                condlog(0, "%s: error getting map status string", name);
@@ -1045,7 +1054,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
        int queue_if_no_path = 0;
        int udev_flags = 0;
        unsigned long long mapsize;
-       char params[PARAMS_SIZE] = {0};
+       char *params = NULL;
 
        if (dm_is_mpath(mapname) != 1)
                return 0; /* nothing to do */
@@ -1061,7 +1070,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
                        return 1;
 
        if (need_suspend &&
-           dm_get_map(mapname, &mapsize, params) == DMP_OK &&
+           dm_get_map(mapname, &mapsize, &params) == DMP_OK &&
            strstr(params, "queue_if_no_path")) {
                if (!dm_queue_if_no_path(mapname, 0))
                        queue_if_no_path = 1;
@@ -1069,6 +1078,8 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
                        /* Leave queue_if_no_path alone if unset failed */
                        queue_if_no_path = -1;
        }
+       free(params);
+       params = NULL;
 
        if (dm_remove_partmaps(mapname, need_sync, deferred_remove))
                return 1;
@@ -1122,7 +1133,8 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove)
 #else
 
 int
-dm_flush_map_nopaths(const char * mapname, int deferred_remove)
+dm_flush_map_nopaths(const char * mapname,
+                    int deferred_remove __attribute__((unused)))
 {
        return _dm_flush_map(mapname, 1, 0, 0, 0);
 }
@@ -1426,7 +1438,7 @@ do_foreach_partmaps (const char * mapname,
        struct dm_task *dmt;
        struct dm_names *names;
        unsigned next = 0;
-       char params[PARAMS_SIZE];
+       char *params = NULL;
        unsigned long long size;
        char dev_t[32];
        int r = 1;
@@ -1469,7 +1481,7 @@ do_foreach_partmaps (const char * mapname,
                    /*
                     * and we can fetch the map table from the kernel
                     */
-                   dm_get_map(names->name, &size, &params[0]) == DMP_OK &&
+                   dm_get_map(names->name, &size, &params) == DMP_OK &&
 
                    /*
                     * and the table maps over the multipath map
@@ -1481,12 +1493,15 @@ do_foreach_partmaps (const char * mapname,
                                goto out;
                }
 
+               free(params);
+               params = NULL;
                next = names->next;
                names = (void *) names + next;
        } while (next);
 
        r = 0;
 out:
+       free(params);
        dm_task_destroy (dmt);
        return r;
 }
@@ -1573,7 +1588,7 @@ dm_cancel_deferred_remove (struct multipath *mpp)
 #else
 
 int
-dm_cancel_deferred_remove (struct multipath *mpp)
+dm_cancel_deferred_remove (struct multipath *mpp __attribute__((unused)))
 {
        return 0;
 }
@@ -1615,17 +1630,19 @@ struct rename_data {
 static int
 rename_partmap (const char *name, void *data)
 {
-       char buff[PARAMS_SIZE];
+       char *buff = NULL;
        int offset;
        struct rename_data *rd = (struct rename_data *)data;
 
        if (strncmp(name, rd->old, strlen(rd->old)) != 0)
                return 0;
        for (offset = strlen(rd->old); name[offset] && !(isdigit(name[offset])); offset++); /* do nothing */
-       snprintf(buff, PARAMS_SIZE, "%s%s%s", rd->new, rd->delim,
-                name + offset);
-       dm_rename(name, buff, rd->delim, SKIP_KPARTX_OFF);
-       condlog(4, "partition map %s renamed", name);
+       if (asprintf(&buff, "%s%s%s", rd->new, rd->delim, name + offset) >= 0) {
+               dm_rename(name, buff, rd->delim, SKIP_KPARTX_OFF);
+               free(buff);
+               condlog(4, "partition map %s renamed", name);
+       } else
+               condlog(1, "failed to rename partition map %s", name);
        return 0;
 }
 
index e29b4d411dddf7c873ad0b1318d657a337698bf4..45a676de28420277bd8342674a08690f16e88961 100644 (file)
@@ -44,8 +44,8 @@ int dm_addmap_create (struct multipath *mpp, char *params);
 int dm_addmap_reload (struct multipath *mpp, char *params, int flush);
 int dm_map_present (const char *);
 int dm_map_present_by_uuid(const char *uuid);
-int dm_get_map(const char *, unsigned long long *, char *);
-int dm_get_status(const char *, char *);
+int dm_get_map(const char *, unsigned long long *, char **);
+int dm_get_status(const char *, char **);
 int dm_type(const char *, char *);
 int dm_is_mpath(const char *);
 int _dm_flush_map (const char *, int, int, int, int);
index dd08abf5ef69b5c0b56457262c3acb7be2c26279..7a72738931d1c1e49a24f201f73b8cf0fc44485e 100644 (file)
@@ -26,6 +26,7 @@
 #include <mpath_persist.h>
 #include "mpath_cmd.h"
 #include "dict.h"
+#include "strbuf.h"
 
 static int
 set_int(vector strvec, void *ptr)
@@ -143,84 +144,45 @@ set_yes_no_undef(vector strvec, void *ptr)
        return 0;
 }
 
-static int
-print_int (char *buff, int len, long v)
+static int print_int(struct strbuf *buff, long v)
 {
-       return snprintf(buff, len, "%li", v);
+       return print_strbuf(buff, "%li", v);
 }
 
-static int
-print_nonzero (char *buff, int len, long v)
+static int print_nonzero(struct strbuf *buff, long v)
 {
        if (!v)
                return 0;
-       return snprintf(buff, len, "%li", v);
+       return print_strbuf(buff, "%li", v);
 }
 
-static int
-print_str (char *buff, int len, const char *ptr)
+static int print_str(struct strbuf *buff, const char *ptr)
 {
-       char *p;
-       char *last;
-       const char *q;
+       int ret = append_strbuf_quoted(buff, ptr);
 
-       if (!ptr || len <= 0)
-               return 0;
-
-       q = strchr(ptr, '"');
-       if (q == NULL)
-               return snprintf(buff, len, "\"%s\"", ptr);
-
-       last = buff + len - 1;
-       p = buff;
-       if (p >= last)
-               goto out;
-       *p++ = '"';
-       if (p >= last)
-               goto out;
-       for (; q; q = strchr(ptr, '"')) {
-               if (q + 1 - ptr < last - p)
-                       p = mempcpy(p, ptr, q + 1 - ptr);
-               else {
-                       p = mempcpy(p, ptr, last - p);
-                       goto out;
-               }
-               *p++ = '"';
-               if (p >= last)
-                       goto out;
-               ptr = q + 1;
-       }
-       p += strlcpy(p, ptr, last - p);
-       if (p >= last)
-               goto out;
-       *p++ = '"';
-       *p = '\0';
-       return p - buff;
-out:
-       *p = '\0';
-       return len;
+       /*
+        * -EINVAL aka (ptr == NULL) means "not set".
+        * Returning an error here breaks unit tests
+        * (logic in snprint_keyword()).
+        */
+       return ret == -EINVAL ? 0 : ret;
 }
 
-static int
-print_ignored (char *buff, int len)
+static int print_ignored(struct strbuf *buff)
 {
-       return snprintf(buff, len, "ignored");
+       return append_strbuf_quoted(buff, "ignored");
 }
 
-static int
-print_yes_no (char *buff, int len, long v)
+static int print_yes_no(struct strbuf *buff, long v)
 {
-       return snprintf(buff, len, "\"%s\"",
-                       (v == YN_NO)? "no" : "yes");
+       return append_strbuf_quoted(buff, v == YN_NO ? "no" : "yes");
 }
 
-static int
-print_yes_no_undef (char *buff, int len, long v)
+static int print_yes_no_undef(struct strbuf *buff, long v)
 {
        if (!v)
                return 0;
-       return snprintf(buff, len, "\"%s\"",
-                       (v == YNU_NO)? "no" : "yes");
+       return append_strbuf_quoted(buff, v == YNU_NO? "no" : "yes");
 }
 
 #define declare_def_handler(option, function)                          \
@@ -232,32 +194,32 @@ def_ ## option ## _handler (struct config *conf, vector strvec)           \
 
 #define declare_def_snprint(option, function)                          \
 static int                                                             \
-snprint_def_ ## option (struct config *conf, char * buff, int len,     \
-                       const void * data)                              \
+snprint_def_ ## option (struct config *conf, struct strbuf *buff,      \
+                       const void *data)                               \
 {                                                                      \
-       return function (buff, len, conf->option);                      \
+       return function(buff, conf->option);                            \
 }
 
 #define declare_def_snprint_defint(option, function, value)            \
 static int                                                             \
-snprint_def_ ## option (struct config *conf, char * buff, int len,     \
-                       const void * data)                              \
+snprint_def_ ## option (struct config *conf, struct strbuf *buff,      \
+                       const void *data)                               \
 {                                                                      \
        int i = value;                                                  \
        if (!conf->option)                                              \
-               return function (buff, len, i);                         \
-       return function (buff, len, conf->option);                      \
+               return function(buff, i);                               \
+       return function (buff, conf->option);                           \
 }
 
 #define declare_def_snprint_defstr(option, function, value)            \
 static int                                                             \
-snprint_def_ ## option (struct config *conf, char * buff, int len,     \
-                       const void * data)                              \
+snprint_def_ ## option (struct config *conf, struct strbuf *buff,      \
+                       const void *data)                               \
 {                                                                      \
        static const char *s = value;                                   \
        if (!conf->option)                                              \
-               return function (buff, len, s);                         \
-       return function (buff, len, conf->option);                      \
+               return function(buff, s);                               \
+       return function(buff, conf->option);                            \
 }
 
 #define declare_hw_handler(option, function)                           \
@@ -272,11 +234,11 @@ hw_ ## option ## _handler (struct config *conf, vector strvec)            \
 
 #define declare_hw_snprint(option, function)                           \
 static int                                                             \
-snprint_hw_ ## option (struct config *conf, char * buff, int len,      \
-                      const void * data)                               \
+snprint_hw_ ## option (struct config *conf, struct strbuf *buff,       \
+                      const void *data)                                \
 {                                                                      \
        const struct hwentry * hwe = (const struct hwentry *)data;      \
-       return function (buff, len, hwe->option);                       \
+       return function(buff, hwe->option);                             \
 }
 
 #define declare_ovr_handler(option, function)                          \
@@ -290,10 +252,10 @@ ovr_ ## option ## _handler (struct config *conf, vector strvec)           \
 
 #define declare_ovr_snprint(option, function)                          \
 static int                                                             \
-snprint_ovr_ ## option (struct config *conf, char * buff, int len,     \
-                       const void * data)                              \
+snprint_ovr_ ## option (struct config *conf, struct strbuf *buff,      \
+                       const void *data)                               \
 {                                                                      \
-       return function (buff, len, conf->overrides->option);           \
+       return function (buff, conf->overrides->option);                \
 }
 
 #define declare_mp_handler(option, function)                           \
@@ -308,11 +270,11 @@ mp_ ## option ## _handler (struct config *conf, vector strvec)            \
 
 #define declare_mp_snprint(option, function)                           \
 static int                                                             \
-snprint_mp_ ## option (struct config *conf, char * buff, int len,      \
-                      const void * data)                               \
+snprint_mp_ ## option (struct config *conf, struct strbuf *buff,       \
+                      const void *data)                                \
 {                                                                      \
        const struct mpentry * mpe = (const struct mpentry *)data;      \
-       return function (buff, len, mpe->option);                       \
+       return function(buff, mpe->option);                             \
 }
 
 static int checkint_handler(struct config *conf, vector strvec)
@@ -354,13 +316,13 @@ static int def_partition_delim_handler(struct config *conf, vector strvec)
        return 0;
 }
 
-static int snprint_def_partition_delim(struct config *conf, char *buff,
-                                      int len, const void *data)
+static int snprint_def_partition_delim(struct config *conf, struct strbuf *buff,
+                                      const void *data)
 {
        if (default_partition_delim == NULL || conf->partition_delim != NULL)
-               return print_str(buff, len, conf->partition_delim);
+               return print_str(buff, conf->partition_delim);
        else
-               return print_str(buff, len, UNSET_PARTITION_DELIM);
+               return print_str(buff, UNSET_PARTITION_DELIM);
 }
 
 static const char * const find_multipaths_optvals[] = {
@@ -403,10 +365,10 @@ def_find_multipaths_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_def_find_multipaths(struct config *conf, char *buff, int len,
+snprint_def_find_multipaths(struct config *conf, struct strbuf *buff,
                            const void *data)
 {
-       return print_str(buff, len,
+       return append_strbuf_quoted(buff,
                         find_multipaths_optvals[conf->find_multipaths]);
 }
 
@@ -419,21 +381,19 @@ declare_ovr_snprint(selector, print_str)
 declare_mp_handler(selector, set_str)
 declare_mp_snprint(selector, print_str)
 
-static int snprint_uid_attrs(struct config *conf, char *buff, int len,
+static int snprint_uid_attrs(struct config *conf, struct strbuf *buff,
                             const void *dummy)
 {
-       char *p = buff;
-       int n, j;
+       int j, ret, total = 0;
        const char *att;
 
        vector_foreach_slot(&conf->uid_attrs, att, j) {
-               n = snprintf(p, len, "%s%s", j == 0 ? "" : " ", att);
-               if (n >= len)
-                       return (p - buff) + n;
-               p += n;
-               len -= n;
+               ret = print_strbuf(buff, "%s%s", j == 0 ? "" : " ", att);
+               if (ret < 0)
+                       return ret;
+               total += ret;
        }
-       return p - buff;
+       return total;
 }
 
 static int uid_attrs_handler(struct config *conf, vector strvec)
@@ -526,18 +486,23 @@ declare_mp_snprint(minio_rq, print_nonzero)
 
 declare_def_handler(queue_without_daemon, set_yes_no)
 static int
-snprint_def_queue_without_daemon (struct config *conf,
-                                 char * buff, int len, const void * data)
+snprint_def_queue_without_daemon(struct config *conf, struct strbuf *buff,
+                                const void * data)
 {
+       const char *qwd = "unknown";
+
        switch (conf->queue_without_daemon) {
        case QUE_NO_DAEMON_OFF:
-               return snprintf(buff, len, "\"no\"");
+               qwd = "no";
+               break;
        case QUE_NO_DAEMON_ON:
-               return snprintf(buff, len, "\"yes\"");
+               qwd = "yes";
+               break;
        case QUE_NO_DAEMON_FORCE:
-               return snprintf(buff, len, "\"forced\"");
+               qwd = "forced";
+               break;
        }
-       return 0;
+       return append_strbuf_quoted(buff, qwd);
 }
 
 declare_def_handler(checker_timeout, set_int)
@@ -636,10 +601,11 @@ static int def_disable_changed_wwids_handler(struct config *conf, vector strvec)
 {
        return 0;
 }
-static int snprint_def_disable_changed_wwids(struct config *conf, char *buff,
-                                            int len, const void *data)
+static int snprint_def_disable_changed_wwids(struct config *conf,
+                                            struct strbuf *buff,
+                                            const void *data)
 {
-       return print_ignored(buff, len);
+       return print_ignored(buff);
 }
 
 declare_def_handler(remove_retries, set_int)
@@ -681,11 +647,10 @@ def_ ## option ## _handler (struct config *conf, vector strvec)           \
 
 #define declare_def_attr_snprint(option, function)                     \
 static int                                                             \
-snprint_def_ ## option (struct config *conf, char * buff, int len,     \
-                       const void * data)                              \
+snprint_def_ ## option (struct config *conf, struct strbuf *buff,      \
+                       const void *data)                               \
 {                                                                      \
-       return function (buff, len, conf->option,                       \
-                        conf->attribute_flags);                        \
+       return function(buff, conf->option, conf->attribute_flags);     \
 }
 
 #define declare_mp_attr_handler(option, function)                      \
@@ -700,12 +665,11 @@ mp_ ## option ## _handler (struct config *conf, vector strvec)            \
 
 #define declare_mp_attr_snprint(option, function)                      \
 static int                                                             \
-snprint_mp_ ## option (struct config *conf, char * buff, int len,      \
+snprint_mp_ ## option (struct config *conf, struct strbuf *buff,       \
                       const void * data)                               \
 {                                                                      \
        const struct mpentry * mpe = (const struct mpentry *)data;      \
-       return function (buff, len, mpe->option,                        \
-                        mpe->attribute_flags);                         \
+       return function(buff, mpe->option, mpe->attribute_flags);       \
 }
 
 static int
@@ -780,30 +744,30 @@ set_gid(vector strvec, void *ptr, int *flags)
 }
 
 static int
-print_mode(char * buff, int len, long v, int flags)
+print_mode(struct strbuf *buff, long v, int flags)
 {
        mode_t mode = (mode_t)v;
        if ((flags & (1 << ATTR_MODE)) == 0)
                return 0;
-       return snprintf(buff, len, "0%o", mode);
+       return print_strbuf(buff, "0%o", mode);
 }
 
 static int
-print_uid(char * buff, int len, long v, int flags)
+print_uid(struct strbuf *buff, long v, int flags)
 {
        uid_t uid = (uid_t)v;
        if ((flags & (1 << ATTR_UID)) == 0)
                return 0;
-       return snprintf(buff, len, "0%o", uid);
+       return print_strbuf(buff, "0%o", uid);
 }
 
 static int
-print_gid(char * buff, int len, long v, int flags)
+print_gid(struct strbuf *buff, long v, int flags)
 {
        gid_t gid = (gid_t)v;
        if ((flags & (1 << ATTR_GID)) == 0)
                return 0;
-       return snprintf(buff, len, "0%o", gid);
+       return print_strbuf(buff, "0%o", gid);
 }
 
 declare_def_attr_handler(mode, set_mode)
@@ -843,16 +807,15 @@ set_undef_off_zero(vector strvec, void *ptr)
        return 0;
 }
 
-int
-print_undef_off_zero(char * buff, int len, long v)
+int print_undef_off_zero(struct strbuf *buff, long v)
 {
        if (v == UOZ_UNDEF)
                return 0;
        if (v == UOZ_OFF)
-               return snprintf(buff, len, "\"off\"");
+               return append_strbuf_str(buff, "off");
        if (v == UOZ_ZERO)
-               return snprintf(buff, len, "0");
-       return snprintf(buff, len, "%ld", v);
+               return append_strbuf_str(buff, "0");
+       return print_int(buff, v);
 }
 
 declare_def_handler(fast_io_fail, set_undef_off_zero)
@@ -883,13 +846,13 @@ set_dev_loss(vector strvec, void *ptr)
 }
 
 int
-print_dev_loss(char * buff, int len, unsigned long v)
+print_dev_loss(struct strbuf *buff, unsigned long v)
 {
        if (v == DEV_LOSS_TMO_UNSET)
                return 0;
        if (v >= MAX_DEV_LOSS_TMO)
-               return snprintf(buff, len, "\"infinity\"");
-       return snprintf(buff, len, "%lu", v);
+               return append_strbuf_quoted(buff, "infinity");
+       return print_strbuf(buff, "%lu", v);
 }
 
 declare_def_handler(dev_loss, set_dev_loss)
@@ -923,7 +886,7 @@ set_pgpolicy(vector strvec, void *ptr)
 }
 
 int
-print_pgpolicy(char * buff, int len, long pgpolicy)
+print_pgpolicy(struct strbuf *buff, long pgpolicy)
 {
        char str[POLICY_NAME_SIZE];
 
@@ -932,7 +895,7 @@ print_pgpolicy(char * buff, int len, long pgpolicy)
 
        get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
 
-       return snprintf(buff, len, "\"%s\"", str);
+       return append_strbuf_quoted(buff, str);
 }
 
 declare_def_handler(pgpolicy, set_pgpolicy)
@@ -1003,7 +966,7 @@ max_fds_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_max_fds (struct config *conf, char * buff, int len, const void * data)
+snprint_max_fds (struct config *conf, struct strbuf *buff, const void *data)
 {
        int r = 0, max_fds;
 
@@ -1012,9 +975,9 @@ snprint_max_fds (struct config *conf, char * buff, int len, const void * data)
 
        r = get_sys_max_fds(&max_fds);
        if (!r && conf->max_fds >= max_fds)
-               return snprintf(buff, len, "\"max\"");
+               return append_strbuf_quoted(buff, "max");
        else
-               return snprintf(buff, len, "%d", conf->max_fds);
+               return print_int(buff, conf->max_fds);
 }
 
 static int
@@ -1040,14 +1003,14 @@ set_rr_weight(vector strvec, void *ptr)
 }
 
 int
-print_rr_weight (char * buff, int len, long v)
+print_rr_weight (struct strbuf *buff, long v)
 {
        if (!v)
                return 0;
        if (v == RR_WEIGHT_PRIO)
-               return snprintf(buff, len, "\"priorities\"");
+               return append_strbuf_quoted(buff, "priorities");
        if (v == RR_WEIGHT_NONE)
-               return snprintf(buff, len, "\"uniform\"");
+               return append_strbuf_quoted(buff, "uniform");
 
        return 0;
 }
@@ -1086,19 +1049,19 @@ set_pgfailback(vector strvec, void *ptr)
 }
 
 int
-print_pgfailback (char * buff, int len, long v)
+print_pgfailback (struct strbuf *buff, long v)
 {
        switch(v) {
        case  FAILBACK_UNDEF:
                return 0;
        case -FAILBACK_MANUAL:
-               return snprintf(buff, len, "\"manual\"");
+               return append_strbuf_quoted(buff, "manual");
        case -FAILBACK_IMMEDIATE:
-               return snprintf(buff, len, "\"immediate\"");
+               return append_strbuf_quoted(buff, "immediate");
        case -FAILBACK_FOLLOWOVER:
-               return snprintf(buff, len, "\"followover\"");
+               return append_strbuf_quoted(buff, "followover");
        default:
-               return snprintf(buff, len, "%li", v);
+               return print_int(buff, v);
        }
 }
 
@@ -1133,17 +1096,17 @@ no_path_retry_helper(vector strvec, void *ptr)
 }
 
 int
-print_no_path_retry(char * buff, int len, long v)
+print_no_path_retry(struct strbuf *buff, long v)
 {
        switch(v) {
        case NO_PATH_RETRY_UNDEF:
                return 0;
        case NO_PATH_RETRY_FAIL:
-               return snprintf(buff, len, "\"fail\"");
+               return append_strbuf_quoted(buff, "fail");
        case NO_PATH_RETRY_QUEUE:
-               return snprintf(buff, len, "\"queue\"");
+               return append_strbuf_quoted(buff, "queue");
        default:
-               return snprintf(buff, len, "%li", v);
+               return print_int(buff, v);
        }
 }
 
@@ -1176,12 +1139,12 @@ def_log_checker_err_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_def_log_checker_err (struct config *conf, char * buff, int len,
-                            const void * data)
+snprint_def_log_checker_err(struct config *conf, struct strbuf *buff,
+                           const void * data)
 {
        if (conf->log_checker_err == LOG_CHKR_ERR_ONCE)
-               return snprintf(buff, len, "once");
-       return snprintf(buff, len, "always");
+               return append_strbuf_quoted(buff, "once");
+       return append_strbuf_quoted(buff, "always");
 }
 
 static int
@@ -1216,18 +1179,17 @@ set_reservation_key(vector strvec, struct be64 *be64_ptr, uint8_t *flags_ptr,
 }
 
 int
-print_reservation_key(char * buff, int len, struct be64 key, uint8_t flags,
-                     int source)
+print_reservation_key(struct strbuf *buff,
+                     struct be64 key, uint8_t flags, int source)
 {
        char *flagstr = "";
        if (source == PRKEY_SOURCE_NONE)
                return 0;
        if (source == PRKEY_SOURCE_FILE)
-               return snprintf(buff, len, "file");
+               return append_strbuf_quoted(buff, "file");
        if (flags & MPATH_F_APTPL_MASK)
                flagstr = ":aptpl";
-       return snprintf(buff, len, "0x%" PRIx64 "%s", get_be64(key),
-                       flagstr);
+       return print_strbuf(buff, "0x%" PRIx64 "%s", get_be64(key), flagstr);
 }
 
 static int
@@ -1239,12 +1201,11 @@ def_reservation_key_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_def_reservation_key (struct config *conf, char * buff, int len,
+snprint_def_reservation_key (struct config *conf, struct strbuf *buff,
                             const void * data)
 {
-       return print_reservation_key(buff, len, conf->reservation_key,
-                                    conf->sa_flags,
-                                    conf->prkey_source);
+       return print_reservation_key(buff, conf->reservation_key,
+                                    conf->sa_flags, conf->prkey_source);
 }
 
 static int
@@ -1259,13 +1220,12 @@ mp_reservation_key_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_mp_reservation_key (struct config *conf, char * buff, int len,
-                           const void * data)
+snprint_mp_reservation_key (struct config *conf, struct strbuf *buff,
+                           const void *data)
 {
        const struct mpentry * mpe = (const struct mpentry *)data;
-       return print_reservation_key(buff, len, mpe->reservation_key,
-                                    mpe->sa_flags,
-                                    mpe->prkey_source);
+       return print_reservation_key(buff, mpe->reservation_key,
+                                    mpe->sa_flags, mpe->prkey_source);
 }
 
 static int
@@ -1288,15 +1248,15 @@ set_off_int_undef(vector strvec, void *ptr)
 }
 
 int
-print_off_int_undef(char * buff, int len, long v)
+print_off_int_undef(struct strbuf *buff, long v)
 {
        switch(v) {
        case NU_UNDEF:
                return 0;
        case NU_NO:
-               return snprintf(buff, len, "\"no\"");
+               return append_strbuf_quoted(buff, "no");
        default:
-               return snprintf(buff, len, "%li", v);
+               return print_int(buff, v);
        }
 }
 
@@ -1455,13 +1415,13 @@ out:
 }
 
 static int
-snprint_hw_vpd_vendor(struct config *conf, char * buff, int len,
+snprint_hw_vpd_vendor(struct config *conf, struct strbuf *buff,
                      const void * data)
 {
        const struct hwentry * hwe = (const struct hwentry *)data;
 
        if (hwe->vpd_vendor_id > 0 && hwe->vpd_vendor_id < VPD_VP_ARRAY_SIZE)
-               return snprintf(buff, len, "%s",
+               return append_strbuf_quoted(buff,
                                vpd_vendor_pages[hwe->vpd_vendor_id].name);
        return 0;
 }
@@ -1561,19 +1521,18 @@ declare_ble_handler(blist_protocol)
 declare_ble_handler(elist_protocol)
 
 static int
-snprint_def_uxsock_timeout(struct config *conf, char * buff, int len,
-                          const void * data)
+snprint_def_uxsock_timeout(struct config *conf, struct strbuf *buff,
+                          const void *data)
 {
-       return snprintf(buff, len, "%u", conf->uxsock_timeout);
+       return print_strbuf(buff, "%u", conf->uxsock_timeout);
 }
 
 static int
-snprint_ble_simple (struct config *conf, char * buff, int len,
-                   const void * data)
+snprint_ble_simple (struct config *conf, struct strbuf *buff, const void *data)
 {
-       const struct blentry * ble = (const struct blentry *)data;
+       const struct blentry *ble = (const struct blentry *)data;
 
-       return snprintf(buff, len, "\"%s\"", ble->str);
+       return print_str(buff, ble->str);
 }
 
 static int
@@ -1593,24 +1552,22 @@ declare_ble_device_handler(vendor, elist_device, buff, NULL)
 declare_ble_device_handler(product, blist_device, NULL, buff)
 declare_ble_device_handler(product, elist_device, NULL, buff)
 
-static int
-snprint_bled_vendor (struct config *conf, char * buff, int len,
-                    const void * data)
+static int snprint_bled_vendor(struct config *conf, struct strbuf *buff,
+                              const void * data)
 {
        const struct blentry_device * bled =
                (const struct blentry_device *)data;
 
-       return snprintf(buff, len, "\"%s\"", bled->vendor);
+       return print_str(buff, bled->vendor);
 }
 
-static int
-snprint_bled_product (struct config *conf, char * buff, int len,
-                     const void * data)
+static int snprint_bled_product(struct config *conf, struct strbuf *buff,
+                               const void *data)
 {
        const struct blentry_device * bled =
                (const struct blentry_device *)data;
 
-       return snprintf(buff, len, "\"%s\"", bled->product);
+       return print_str(buff, bled->product);
 }
 
 /*
@@ -1738,8 +1695,7 @@ deprecated_handler(struct config *conf, vector strvec)
 }
 
 static int
-snprint_deprecated (struct config *conf, char * buff, int len,
-                   const void * data)
+snprint_deprecated (struct config *conf, struct strbuf *buff, const void * data)
 {
        return 0;
 }
index a917e1cab1857f071f62d9eb381e2a95c3b3d1a4..d80f990a7d1562334f8c4f402d7eb0cf6d5432b6 100644 (file)
@@ -6,16 +6,17 @@
 #endif
 
 #include "byteorder.h"
+struct strbuf;
 
 void init_keywords(vector keywords);
 int get_sys_max_fds(int *);
-int print_rr_weight(char *buff, int len, long v);
-int print_pgfailback(char *buff, int len, long v);
-int print_pgpolicy(char *buff, int len, long v);
-int print_no_path_retry(char *buff, int len, long v);
-int print_undef_off_zero(char *buff, int len, long v);
-int print_dev_loss(char *buff, int len, unsigned long v);
-int print_reservation_key(char * buff, int len, struct be64 key, uint8_t
-                         flags, int source);
-int print_off_int_undef(char *buff, int len, long v);
+int print_rr_weight(struct strbuf *buff, long v);
+int print_pgfailback(struct strbuf *buff, long v);
+int print_pgpolicy(struct strbuf *buff, long v);
+int print_no_path_retry(struct strbuf *buff, long v);
+int print_undef_off_zero(struct strbuf *buff, long v);
+int print_dev_loss(struct strbuf *buff, unsigned long v);
+int print_reservation_key(struct strbuf *buff,
+                         struct be64 key, uint8_t flags, int source);
+int print_off_int_undef(struct strbuf *buff, long v);
 #endif /* _DICT_H */
index ec99a7aa5d5ba3cc48f0881e5bd8dc233b30ecd7..f25fe9e3496a836cffd9a7571bdfd997168dc2a4 100644 (file)
@@ -35,6 +35,7 @@
 #include "foreign.h"
 #include "configure.h"
 #include "print.h"
+#include "strbuf.h"
 
 struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = {
        [VPD_VP_UNDEF]  = { 0x00, "undef" },
@@ -635,7 +636,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
 {
        struct udev_device *rport_dev = NULL;
        char value[16], *eptr;
-       char rport_id[32];
+       char rport_id[42];
        unsigned int tmo;
        int ret;
 
@@ -895,11 +896,11 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
        }
 
        if (err_path) {
-               char proto_buf[32];
+               STRBUF_ON_STACK(proto_buf);
 
-               snprint_path_protocol(proto_buf, sizeof(proto_buf), err_path);
+               snprint_path_protocol(&proto_buf, err_path);
                condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
-                       mpp->alias, proto_buf);
+                       mpp->alias, get_strbuf_str(&proto_buf));
        }
        return 0;
 }
@@ -1427,7 +1428,7 @@ scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
                        attr_path = udev_device_get_sysname(parent);
                        if (!attr_path)
                                break;
-                       if (sscanf(attr_path, "%i:%i:%i:%i",
+                       if (sscanf(attr_path, "%i:%i:%i:%" SCNu64,
                                   &pp->sg_id.host_no,
                                   &pp->sg_id.channel,
                                   &pp->sg_id.scsi_id,
@@ -1462,7 +1463,7 @@ scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
        /*
         * host / bus / target / lun
         */
-       condlog(3, "%s: h:b:t:l = %i:%i:%i:%i",
+       condlog(3, "%s: h:b:t:l = %i:%i:%i:%" PRIu64,
                        pp->dev,
                        pp->sg_id.host_no,
                        pp->sg_id.channel,
@@ -1577,7 +1578,7 @@ ccw_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
                   &pp->sg_id.host_no,
                   &pp->sg_id.channel,
                   &pp->sg_id.scsi_id) == 3) {
-               condlog(3, "%s: h:b:t:l = %i:%i:%i:%i",
+               condlog(3, "%s: h:b:t:l = %i:%i:%i:%" PRIu64,
                        pp->dev,
                        pp->sg_id.host_no,
                        pp->sg_id.channel,
@@ -1636,7 +1637,7 @@ cciss_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
         */
        pp->sg_id.lun = 0;
        pp->sg_id.channel = 0;
-       condlog(3, "%s: h:b:t:l = %i:%i:%i:%i",
+       condlog(3, "%s: h:b:t:l = %i:%i:%i:%" PRIu64,
                pp->dev,
                pp->sg_id.host_no,
                pp->sg_id.channel,
@@ -1815,7 +1816,7 @@ scsi_ioctl_pathinfo (struct path * pp, int mask)
                        attr_path = udev_device_get_sysname(parent);
                        if (!attr_path)
                                break;
-                       if (sscanf(attr_path, "%i:%i:%i:%i",
+                       if (sscanf(attr_path, "%i:%i:%i:%" SCNu64,
                                   &pp->sg_id.host_no,
                                   &pp->sg_id.channel,
                                   &pp->sg_id.scsi_id,
@@ -2380,11 +2381,11 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                                 * It's likely that this path is not fit for
                                 * multipath use.
                                 */
-                               char buf[16];
+                               STRBUF_ON_STACK(buf);
 
-                               snprint_path(buf, sizeof(buf), "%T", pp, 0);
+                               snprint_path(&buf, "%T", pp, 0);
                                condlog(1, "%s: no WWID in state \"%s\", giving up",
-                                       pp->dev, buf);
+                                       pp->dev, get_strbuf_str(&buf));
                                return PATHINFO_SKIPPED;
                        }
                        return PATHINFO_OK;
index b306c46b61ba2c1e8d3230331d234722ae493e83..4ba7f339940d1a252c762f5f33c7ca46a7a1ad19 100644 (file)
@@ -15,6 +15,7 @@
 #include "util.h"
 #include "debug.h"
 #include "dmparser.h"
+#include "strbuf.h"
 
 #define WORD_SIZE 64
 
@@ -41,40 +42,21 @@ merge_words(char **dst, const char *word)
        return 0;
 }
 
-#define APPEND(p, end, args...)                                                \
-({                                                                     \
-       int ret;                                                        \
-                                                                       \
-       ret = snprintf(p, end - p, ##args);                             \
-       if (ret < 0) {                                                  \
-               condlog(0, "%s: conversion error", mp->alias);          \
-               goto err;                                               \
-       }                                                               \
-       p += ret;                                                       \
-       if (p >= end) {                                                 \
-               condlog(0, "%s: params too small", mp->alias);          \
-               goto err;                                               \
-       }                                                               \
-})
-
 /*
  * Transforms the path group vector into a proper device map string
  */
-int
-assemble_map (struct multipath * mp, char * params, int len)
+int assemble_map(struct multipath *mp, char **params)
 {
+       static const char no_path_retry[] = "queue_if_no_path";
+       static const char retain_hwhandler[] = "retain_attached_hw_handler";
        int i, j;
        int minio;
        int nr_priority_groups, initial_pg_nr;
-       char * p;
-       const char *const end = params + len;
-       char no_path_retry[] = "queue_if_no_path";
-       char retain_hwhandler[] = "retain_attached_hw_handler";
+       STRBUF_ON_STACK(buff);
        struct pathgroup * pgp;
        struct path * pp;
 
        minio = mp->minio;
-       p = params;
 
        nr_priority_groups = VECTOR_SIZE(mp->pg);
        initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0);
@@ -87,14 +69,15 @@ assemble_map (struct multipath * mp, char * params, int len)
            get_linux_version_code() < KERNEL_VERSION(4, 3, 0))
                add_feature(&mp->features, retain_hwhandler);
 
-       /* mp->features must not be NULL */
-       APPEND(p, end, "%s %s %i %i", mp->features, mp->hwhandler,
-               nr_priority_groups, initial_pg_nr);
+       if (print_strbuf(&buff, "%s %s %i %i", mp->features, mp->hwhandler,
+                        nr_priority_groups, initial_pg_nr) < 0)
+               goto err;
 
        vector_foreach_slot (mp->pg, pgp, i) {
                pgp = VECTOR_SLOT(mp->pg, i);
-               APPEND(p, end, " %s %i 1", mp->selector,
-                      VECTOR_SIZE(pgp->paths));
+               if (print_strbuf(&buff, " %s %i 1", mp->selector,
+                                VECTOR_SIZE(pgp->paths)) < 0)
+                       goto err;
 
                vector_foreach_slot (pgp->paths, pp, j) {
                        int tmp_minio = minio;
@@ -106,19 +89,19 @@ assemble_map (struct multipath * mp, char * params, int len)
                                condlog(0, "dev_t not set for '%s'", pp->dev);
                                goto err;
                        }
-                       APPEND(p, end, " %s %d", pp->dev_t, tmp_minio);
+                       if (print_strbuf(&buff, " %s %d", pp->dev_t, tmp_minio) < 0)
+                               goto err;
                }
        }
 
-       condlog(4, "%s: assembled map [%s]", mp->alias, params);
+       *params = steal_strbuf_str(&buff);
+       condlog(4, "%s: assembled map [%s]", mp->alias, *params);
        return 0;
 
 err:
        return 1;
 }
 
-#undef APPEND
-
 /*
  * Caution callers: If this function encounters yet unkown path devices, it
  * adds them uninitialized to the mpp.
index 212fee5e9731024089a689b7eca2b749ce2ab857..666ae74eda84de6167a05aa99c141e8e9832b667 100644 (file)
@@ -1,3 +1,3 @@
-int assemble_map (struct multipath *, char *, int);
+int assemble_map (struct multipath *, char **);
 int disassemble_map (const struct _vector *, const char *, struct multipath *);
 int disassemble_status (const char *, struct multipath *);
index fce1934727020a7328cad08b876168ecfcaf029f..e091a1dc3868897112af21afc3bec6111e8a25da 100644 (file)
@@ -34,6 +34,7 @@
 #include "structs.h"
 #include "structs_vec.h"
 #include "print.h"
+#include "strbuf.h"
 
 static vector foreigns;
 
@@ -497,11 +498,11 @@ void foreign_multipath_layout(void)
        pthread_cleanup_pop(1);
 }
 
-int snprint_foreign_topology(char *buf, int len, int verbosity)
+int snprint_foreign_topology(struct strbuf *buf, int verbosity)
 {
        struct foreign *fgn;
        int i;
-       char *c = buf;
+       size_t initial_len = get_strbuf_len(buf);
 
        rdlock_foreigns();
        if (foreigns == NULL) {
@@ -521,58 +522,32 @@ int snprint_foreign_topology(char *buf, int len, int verbosity)
                vec = fgn->get_multipaths(fgn->context);
                if (vec != NULL) {
                        vector_foreach_slot(vec, gm, j) {
-
-                               c += _snprint_multipath_topology(gm, c,
-                                                                buf + len - c,
-                                                                verbosity);
-                               if (c >= buf + len - 1)
+                               if (_snprint_multipath_topology(
+                                           gm, buf, verbosity) < 0)
                                        break;
                        }
-                       if (c >= buf + len - 1)
-                               break;
                }
                fgn->release_multipaths(fgn->context, vec);
                pthread_cleanup_pop(1);
        }
 
        pthread_cleanup_pop(1);
-       return c - buf;
+       return get_strbuf_len(buf) - initial_len;
 }
 
 void print_foreign_topology(int verbosity)
 {
-       int buflen = MAX_LINE_LEN * MAX_LINES;
-       char *buf = NULL, *tmp = NULL;
-
-       buf = calloc(1, buflen);
-
-       while (buf != NULL) {
-               char *c = buf;
-
-               c += snprint_foreign_topology(buf, buflen,
-                                                  verbosity);
-               if (c < buf + buflen - 1)
-                       break;
-
-               buflen *= 2;
-               tmp = buf;
-               buf = realloc(buf, buflen);
-       }
+       STRBUF_ON_STACK(buf);
 
-       if (buf == NULL && tmp != NULL)
-               buf = tmp;
-
-       if (buf != NULL) {
-               printf("%s", buf);
-               free(buf);
-       }
+       snprint_foreign_topology(&buf, verbosity);
+       printf("%s", get_strbuf_str(&buf));
 }
 
-int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
+int snprint_foreign_paths(struct strbuf *buf, const char *style, int pretty)
 {
        struct foreign *fgn;
        int i;
-       char *c = buf;
+       size_t initial_len = get_strbuf_len(buf);
 
        rdlock_foreigns();
        if (foreigns == NULL) {
@@ -584,7 +559,7 @@ int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
        vector_foreach_slot(foreigns, fgn, i) {
                const struct _vector *vec;
                const struct gen_path *gp;
-               int j;
+               int j, ret = 0;
 
                fgn->lock(fgn->context);
                pthread_cleanup_push(fgn->unlock, fgn->context);
@@ -592,28 +567,27 @@ int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
                vec = fgn->get_paths(fgn->context);
                if (vec != NULL) {
                        vector_foreach_slot(vec, gp, j) {
-                               c += _snprint_path(gp, c, buf + len - c,
-                                                  style, pretty);
-                               if (c >= buf + len - 1)
+                               ret = _snprint_path(gp, buf, style, pretty);
+                               if (ret < 0)
                                        break;
                        }
-                       if (c >= buf + len - 1)
-                               break;
                }
                fgn->release_paths(fgn->context, vec);
                pthread_cleanup_pop(1);
+               if (ret < 0)
+                       break;
        }
 
        pthread_cleanup_pop(1);
-       return c - buf;
+       return get_strbuf_len(buf) - initial_len;
 }
 
-int snprint_foreign_multipaths(char *buf, int len,
+int snprint_foreign_multipaths(struct strbuf *buf,
                               const char *style, int pretty)
 {
        struct foreign *fgn;
        int i;
-       char *c = buf;
+       size_t initial_len = get_strbuf_len(buf);
 
        rdlock_foreigns();
        if (foreigns == NULL) {
@@ -625,7 +599,7 @@ int snprint_foreign_multipaths(char *buf, int len,
        vector_foreach_slot(foreigns, fgn, i) {
                const struct _vector *vec;
                const struct gen_multipath *gm;
-               int j;
+               int j, ret = 0;
 
                fgn->lock(fgn->context);
                pthread_cleanup_push(fgn->unlock, fgn->context);
@@ -633,18 +607,18 @@ int snprint_foreign_multipaths(char *buf, int len,
                vec = fgn->get_multipaths(fgn->context);
                if (vec != NULL) {
                        vector_foreach_slot(vec, gm, j) {
-                               c += _snprint_multipath(gm, c, buf + len - c,
-                                                       style, pretty);
-                               if (c >= buf + len - 1)
+                               ret = _snprint_multipath(gm, buf,
+                                                        style, pretty);
+                               if (ret < 0)
                                        break;
                        }
-                       if (c >= buf + len - 1)
-                               break;
                }
                fgn->release_multipaths(fgn->context, vec);
                pthread_cleanup_pop(1);
+               if (ret < 0)
+                       break;
        }
 
        pthread_cleanup_pop(1);
-       return c - buf;
+       return get_strbuf_len(buf) - initial_len;
 }
index acd336016740317a0460ce9b62398b6231978a18..77fc4851c9156afa7c51647d775a5b7dad2de854 100644 (file)
@@ -18,9 +18,9 @@
 #define _FOREIGN_H
 #include <stdbool.h>
 #include <libudev.h>
+#define LIBMP_FOREIGN_API ((1 << 8) | 1)
 
-#define LIBMP_FOREIGN_API ((1 << 8) | 0)
-
+struct strbuf;
 struct context;
 
 /* return codes of functions below returning "int" */
@@ -267,35 +267,32 @@ void foreign_multipath_layout(void);
  * prints topology information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param verbosity: verbosity level
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_topology(char *buf, int len, int verbosity);
+int snprint_foreign_topology(struct strbuf *buf, int verbosity);
 
 /**
  * snprint_foreign_paths(buf, len, style, pad);
  * prints formatted path information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param style: format string
  * @param pad: whether to pad field width
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_paths(char *buf, int len, const char *style, int pad);
+int snprint_foreign_paths(struct strbuf *buf, const char *style, int pad);
 
 /**
  * snprint_foreign_multipaths(buf, len, style, pad);
  * prints formatted map information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param style: format string
  * @param pad: whether to pad field width
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_multipaths(char *buf, int len,
+int snprint_foreign_multipaths(struct strbuf *buf,
                               const char *style, int pretty);
 
 /**
index b726be2a53e79c88e546fb7151cade67905db781..d40c0869fc913bcee67f757c87cc232f05bdd6d3 100644 (file)
@@ -37,6 +37,7 @@
 #include "debug.h"
 #include "structs.h"
 #include "sysfs.h"
+#include "strbuf.h"
 
 static const char nvme_vendor[] = "NVMe";
 static const char N_A[] = "n/a";
@@ -138,7 +139,7 @@ static void rstrip(char *str)
 }
 
 static int snprint_nvme_map(const struct gen_multipath *gmp,
-                           char *buff, int len, char wildcard)
+                           struct strbuf *buff, char wildcard)
 {
        const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
        char fld[NAME_LEN];
@@ -146,26 +147,26 @@ static int snprint_nvme_map(const struct gen_multipath *gmp,
 
        switch (wildcard) {
        case 'd':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysname(nvm->udev));
        case 'n':
-               return snprintf(buff, len, "%s:nsid.%s",
+               return print_strbuf(buff, "%s:nsid.%s",
                                udev_device_get_sysattr_value(nvm->subsys,
                                                              "subsysnqn"),
                                udev_device_get_sysattr_value(nvm->udev,
                                                              "nsid"));
        case 'w':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysattr_value(nvm->udev,
                                                              "wwid"));
        case 'N':
-               return snprintf(buff, len, "%u", nvm->nr_live);
+               return print_strbuf(buff, "%u", nvm->nr_live);
        case 'S':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysattr_value(nvm->udev,
                                                              "size"));
        case 'v':
-               return snprintf(buff, len, "%s", nvme_vendor);
+               return append_strbuf_str(buff, nvme_vendor);
        case 's':
        case 'p':
                snprintf(fld, sizeof(fld), "%s",
@@ -173,30 +174,30 @@ static int snprint_nvme_map(const struct gen_multipath *gmp,
                                                      "model"));
                rstrip(fld);
                if (wildcard == 'p')
-                       return snprintf(buff, len, "%s", fld);
-               return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
+                       return append_strbuf_str(buff, fld);
+               return print_strbuf(buff, "%s,%s,%s", nvme_vendor, fld,
                                udev_device_get_sysattr_value(nvm->subsys,
                                                              "firmware_rev"));
        case 'e':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysattr_value(nvm->subsys,
                                                              "firmware_rev"));
        case 'r':
                val = udev_device_get_sysattr_value(nvm->udev, "ro");
                if (val[0] == 1)
-                       return snprintf(buff, len, "%s", "ro");
+                       return append_strbuf_str(buff, "ro");
                else
-                       return snprintf(buff, len, "%s", "rw");
+                       return append_strbuf_str(buff, "rw");
        case 'G':
-               return snprintf(buff, len, "%s", THIS);
+               return append_strbuf_str(buff, THIS);
        case 'h':
                if (nvm->ana_supported == YNU_YES)
-                       return snprintf(buff, len, "ANA");
+                       return append_strbuf_str(buff, "ANA");
        default:
                break;
        }
 
-       return snprintf(buff, len, N_A);
+       return append_strbuf_str(buff, N_A);
 }
 
 static const struct _vector*
@@ -214,7 +215,7 @@ nvme_pg_rel_paths(__attribute__((unused)) const struct gen_pathgroup *gpg,
        /* empty */
 }
 
-static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
+static int snprint_hcil(const struct nvme_path *np, struct strbuf *buf)
 {
        unsigned int nvmeid, ctlid, nsid;
        int rc;
@@ -223,14 +224,13 @@ static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
        rc = sscanf(sysname, "nvme%uc%un%u", &nvmeid, &ctlid, &nsid);
        if (rc != 3) {
                condlog(1, "%s: failed to scan %s", __func__, sysname);
-               rc = snprintf(buf, len, "(ERR:%s)", sysname);
+               return print_strbuf(buf, "(ERR:%s)", sysname);
        } else
-               rc = snprintf(buf, len, "%u:%u:%u", nvmeid, ctlid, nsid);
-       return (rc < len ? rc : len);
+               return print_strbuf(buf, "%u:%u:%u", nvmeid, ctlid, nsid);
 }
 
 static int snprint_nvme_path(const struct gen_path *gp,
-                            char *buff, int len, char wildcard)
+                            struct strbuf *buff, char wildcard)
 {
        const struct nvme_path *np = const_gen_path_to_nvme(gp);
        dev_t devt;
@@ -239,37 +239,37 @@ static int snprint_nvme_path(const struct gen_path *gp,
 
        switch (wildcard) {
        case 'w':
-               return snprintf(buff, len, "%s",
-                               udev_device_get_sysattr_value(np->udev,
-                                                             "wwid"));
+               return print_strbuf(buff, "%s",
+                                   udev_device_get_sysattr_value(np->udev,
+                                                                 "wwid"));
        case 'd':
-               return snprintf(buff, len, "%s",
-                               udev_device_get_sysname(np->udev));
+               return print_strbuf(buff, "%s",
+                                   udev_device_get_sysname(np->udev));
        case 'i':
-               return snprint_hcil(np, buff, len);
+               return snprint_hcil(np, buff);
        case 'D':
                devt = udev_device_get_devnum(np->udev);
-               return snprintf(buff, len, "%u:%u", major(devt), minor(devt));
+               return print_strbuf(buff, "%u:%u", major(devt), minor(devt));
        case 'o':
                if (sysfs_attr_get_value(np->ctl, "state",
                                         fld, sizeof(fld)) > 0)
-                       return snprintf(buff, len, "%s", fld);
+                       return append_strbuf_str(buff, fld);
                break;
        case 'T':
                if (sysfs_attr_get_value(np->udev, "ana_state", fld,
                                         sizeof(fld)) > 0)
-                       return snprintf(buff, len, "%s", fld);
+                       return append_strbuf_str(buff, fld);
                break;
        case 'p':
                if (sysfs_attr_get_value(np->udev, "ana_state", fld,
                                         sizeof(fld)) > 0) {
                        rstrip(fld);
                        if (!strcmp(fld, "optimized"))
-                               return snprintf(buff, len, "%d", 50);
+                               return print_strbuf(buff, "%d", 50);
                        else if (!strcmp(fld, "non-optimized"))
-                               return snprintf(buff, len, "%d", 10);
+                               return print_strbuf(buff, "%d", 10);
                        else
-                               return snprintf(buff, len, "%d", 0);
+                               return print_strbuf(buff, "%d", 0);
                }
                break;
        case 's':
@@ -277,46 +277,45 @@ static int snprint_nvme_path(const struct gen_path *gp,
                         udev_device_get_sysattr_value(np->ctl,
                                                      "model"));
                rstrip(fld);
-               return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
-                               udev_device_get_sysattr_value(np->ctl,
+               return print_strbuf(buff, "%s,%s,%s", nvme_vendor, fld,
+                                   udev_device_get_sysattr_value(np->ctl,
                                                              "firmware_rev"));
        case 'S':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                        udev_device_get_sysattr_value(np->udev,
                                                      "size"));
        case 'z':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysattr_value(np->ctl,
                                                              "serial"));
        case 'm':
-               return snprintf(buff, len, "%s",
+               return append_strbuf_str(buff,
                                udev_device_get_sysname(np->map->udev));
        case 'N':
        case 'R':
-               return snprintf(buff, len, "%s:%s",
+               return print_strbuf(buff, "%s:%s",
                        udev_device_get_sysattr_value(np->ctl,
                                                      "transport"),
                        udev_device_get_sysattr_value(np->ctl,
                                                      "address"));
        case 'G':
-               return snprintf(buff, len, "[%s]", THIS);
+               return print_strbuf(buff, "[%s]", THIS);
        case 'a':
                pci = udev_device_get_parent_with_subsystem_devtype(np->ctl,
                                                                    "pci",
                                                                    NULL);
                if (pci != NULL)
-                       return snprintf(buff, len, "PCI:%s",
-                                       udev_device_get_sysname(pci));
+                       return print_strbuf(buff, "PCI:%s",
+                                           udev_device_get_sysname(pci));
                /* fall through */
        default:
                break;
        }
-       return snprintf(buff, len, "%s", N_A);
-       return 0;
+       return append_strbuf_str(buff, N_A);
 }
 
 static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
-                          char *buff, int len, char wildcard)
+                          struct strbuf *buff, char wildcard)
 {
        const struct nvme_pathgroup *pg = const_gen_pg_to_nvme(gmp);
        const struct nvme_path *path = nvme_pg_to_path(pg);
@@ -324,22 +323,19 @@ static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
        switch (wildcard) {
        case 't':
                return snprint_nvme_path(nvme_path_to_gen(path),
-                                        buff, len, 'T');
+                                        buff, 'T');
        case 'p':
                return snprint_nvme_path(nvme_path_to_gen(path),
-                                        buff, len, 'p');
+                                        buff, 'p');
        default:
-               return snprintf(buff, len, N_A);
+               return append_strbuf_str(buff, N_A);
        }
 }
 
 static int nvme_style(__attribute__((unused)) const struct gen_multipath* gm,
-                     char *buf, int len,
-                     __attribute__((unused)) int verbosity)
+                     struct strbuf *buf, __attribute__((unused)) int verbosity)
 {
-       int n = snprintf(buf, len, "%%w [%%G]:%%d %%s");
-
-       return (n < len ? n : len - 1);
+       return append_strbuf_str(buf, "%%w [%%G]:%%d %%s");
 }
 
 static const struct gen_multipath_ops nvme_map_ops = {
index 5f03b9e05b896e8426e9f6dcca55df10d5dfdb71..e7cf29755ab44422cfef3c591d54b9d31ee7129f 100644 (file)
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-
-#include <string.h>
 #include "generic.h"
 #include "structs.h"
+#include "util.h"
+#include "strbuf.h"
 
-int generic_style(const struct gen_multipath* gm,
-                 char *buf, int len, __attribute__((unused)) int verbosity)
+int generic_style(const struct gen_multipath* gm, struct strbuf *buf,
+                 __attribute__((unused)) int verbosity)
 {
-       char alias_buf[WWID_SIZE];
-       char wwid_buf[WWID_SIZE];
-       int n = 0;
-
-       gm->ops->snprint(gm, alias_buf, sizeof(alias_buf), 'n');
-       gm->ops->snprint(gm, wwid_buf, sizeof(wwid_buf), 'w');
+       STRBUF_ON_STACK(tmp);
+       char *alias_buf __attribute__((cleanup(cleanup_charp)));
+       const char *wwid_buf;
 
-       n += snprintf(buf, len, "%%n %s[%%G]:%%d %%s",
-                     strcmp(alias_buf, wwid_buf) ? "(%w) " : "");
+       gm->ops->snprint(gm, &tmp, 'n');
+       alias_buf = steal_strbuf_str(&tmp);
+       gm->ops->snprint(gm, &tmp, 'w');
+       wwid_buf = get_strbuf_str(&tmp);
 
-       return (n < len ? n : len - 1);
+       return print_strbuf(buf, "%%n %s[%%G]:%%d %%s",
+                           strcmp(alias_buf, wwid_buf) ? "(%w) " : "");
 }
index 6346ffe4334d1d1fe49a013c5dd482949161eb83..57c123ca39e56a3e0f73a98842c1eaa816ccdd8f 100644 (file)
@@ -18,6 +18,7 @@
 #define _GENERIC_H
 #include "vector.h"
 
+struct strbuf;
 struct gen_multipath;
 struct gen_pathgroup;
 struct gen_path;
@@ -50,26 +51,24 @@ struct gen_multipath_ops {
         * 0-terminated, no more than "len" characters including trailing '\0'.
         *
         * @param gmp: generic multipath object to act on
-        * @param buf: output buffer
-        * @param buflen: buffer size
+        * @param buf: output struct strbuf
         * @param wildcard: the multipath wildcard (see print.c)
         * @returns the number of characters printed (without trailing '\0').
         */
        int (*snprint)(const struct gen_multipath*,
-                      char *buf, int len, char wildcard);
+                      struct strbuf *buf, char wildcard);
        /**
         * method: style(gmp, buf, len, verbosity)
         * returns the format string to be used for the multipath object,
         * defined with the wildcards as defined in print.c
         * generic_style() should work well in most cases.
         * @param gmp: generic multipath object to act on
-        * @param buf: output buffer
-        * @param buflen: buffer size
+        * @param buf: output strbuf
         * @param verbosity: verbosity level
         * @returns number of format chars printed
         */
        int (*style)(const struct gen_multipath*,
-                    char *buf, int len, int verbosity);
+                    struct strbuf *buf, int verbosity);
 };
 
 /**
@@ -95,7 +94,7 @@ struct gen_pathgroup_ops {
         * see gen_multipath_ops->snprint() above
         */
        int (*snprint)(const struct gen_pathgroup*,
-                      char *buf, int len, char wildcard);
+                      struct strbuf *buf, char wildcard);
 };
 
 struct gen_path_ops {
@@ -104,7 +103,7 @@ struct gen_path_ops {
         * see gen_multipath_ops->snprint() above
         */
        int (*snprint)(const struct gen_path*,
-                      char *buf, int len, char wildcard);
+                      struct strbuf *buf, char wildcard);
 };
 
 struct gen_multipath {
@@ -129,6 +128,6 @@ struct gen_path {
  * foreign libraries.
  */
 int generic_style(const struct gen_multipath*,
-                 char *buf, int len, int verbosity);
+                 struct strbuf *buf, int verbosity);
 
 #endif /* _GENERIC_H */
index 58fa7387558a90371affb85023fd6765a66f86f1..0caac0da9c558d14fb98ee54d218c5cf68ba67ca 100644 (file)
@@ -107,7 +107,7 @@ static struct hwentry default_hw[] = {
         * HPE
         */
        {
-               /* 3PAR / Primera */
+               /* 3PAR / Primera / Alletra 9000 */
                .vendor        = "3PARdata",
                .product       = "VV",
                .pgpolicy      = GROUP_BY_PRIO,
@@ -225,7 +225,7 @@ static struct hwentry default_hw[] = {
                .prio_name     = PRIO_ALUA,
        },
        {
-               /* Nimble Storage */
+               /* Nimble Storage / HPE Alletra 6000 */
                .vendor        = "Nimble",
                .product       = "Server",
                .hwhandler     = "1 alua",
@@ -659,7 +659,8 @@ static struct hwentry default_hw[] = {
                .pgpolicy      = MULTIBUS,
        },
        {
-               /* Storwize family / SAN Volume Controller / Flex System V7000 / FlashSystem V840/V9000/9100 */
+               // Storwize V5000 and V7000 lines / SAN Volume Controller (SVC) / Flex System V7000 /
+               // FlashSystem V840/V9000/5000/5100/5200/7200/9100/9200/9200R
                .vendor        = "IBM",
                .product       = "^2145",
                .no_path_retry = NO_PATH_RETRY_QUEUE,
@@ -1078,11 +1079,14 @@ static struct hwentry default_hw[] = {
         * Huawei
         */
        {
-               /* OceanStor V3 */
+               /* OceanStor V3-V6 */
+               // This config works with multibus and ALUA
+               // ALUA is required by HyperMetro
                .vendor        = "HUAWEI",
                .product       = "XSG1",
                .pgpolicy      = GROUP_BY_PRIO,
-               .prio_name     = PRIO_ALUA,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .no_path_retry = 15,
        },
        /*
         * Kove
index 0cff3111187cad818cf6ae6b65d63b3c85df927f..eb5b5b55a74649af7b3d8ae146f8b240869d42cd 100644 (file)
@@ -31,7 +31,7 @@
  *   The new version inherits the previous ones.
  */
 
-LIBMULTIPATH_5.0.0 {
+LIBMULTIPATH_9.0.0 {
 global:
        /* symbols referenced by multipath and multipathd */
        add_foreign;
@@ -58,8 +58,6 @@ global:
        count_active_paths;
        delete_all_foreign;
        delete_foreign;
-       disassemble_map;
-       disassemble_status;
        dlog;
        dm_cancel_deferred_remove;
        dm_enablegroup;
@@ -70,10 +68,8 @@ global:
        dm_geteventnr;
        dm_get_info;
        dm_get_major_minor;
-       dm_get_map;
        dm_get_maps;
        dm_get_multipath;
-       dm_get_status;
        dm_get_uuid;
        dm_is_mpath;
        dm_mapname;
@@ -271,6 +267,23 @@ global:
        /* 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:
        *;
 };
index c70243c320a0908fc784c043e333c6265ec2ab94..8ca91bf252842a964808cc9ed5c9bb2a5f419e8b 100644 (file)
@@ -25,6 +25,7 @@
 #include "parser.h"
 #include "memory.h"
 #include "debug.h"
+#include "strbuf.h"
 
 /* local vars */
 static int sublevel = 0;
@@ -33,7 +34,7 @@ static int line_nr;
 int
 keyword_alloc(vector keywords, char *string,
              int (*handler) (struct config *, vector),
-             int (*print) (struct config *, char *, int, const void*),
+             print_fn *print,
              int unique)
 {
        struct keyword *keyword;
@@ -72,7 +73,7 @@ install_sublevel_end(void)
 int
 _install_keyword(vector keywords, char *string,
                 int (*handler) (struct config *, vector),
-                int (*print) (struct config *, char *, int, const void*),
+                print_fn *print,
                 int unique)
 {
        int i = 0;
@@ -149,46 +150,49 @@ find_keyword(vector keywords, vector v, char * name)
 }
 
 int
-snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
                const void *data)
 {
        int r;
-       int fwd = 0;
-       char *f = fmt;
+       char *f;
        struct config *conf;
+       STRBUF_ON_STACK(sbuf);
 
        if (!kw || !kw->print)
                return 0;
 
        do {
-               if (fwd == len || *f == '\0')
-                       break;
-               if (*f != '%') {
-                       *(buff + fwd) = *f;
-                       fwd++;
-                       continue;
+               f = strchr(fmt, '%');
+               if (f == NULL) {
+                       r = append_strbuf_str(&sbuf, fmt);
+                       goto out;
                }
-               f++;
-               switch(*f) {
+               if (f != fmt &&
+                   (r = __append_strbuf_str(&sbuf, fmt, f - fmt)) < 0)
+                       goto out;
+               fmt = f + 1;
+               switch(*fmt) {
                case 'k':
-                       fwd += snprintf(buff + fwd, len - fwd, "%s", kw->string);
+                       if ((r = append_strbuf_str(&sbuf, kw->string)) < 0)
+                               goto out;
                        break;
                case 'v':
                        conf = get_multipath_config();
                        pthread_cleanup_push(put_multipath_config, conf);
-                       r = kw->print(conf, buff + fwd, len - fwd, data);
+                       r = kw->print(conf, &sbuf, data);
                        pthread_cleanup_pop(1);
-                       if (!r) { /* no output if no value */
-                               buff[0] = '\0';
-                               return 0;
+                       if (r < 0)
+                               goto out;
+                       else if (r == 0) {/* no output if no value */
+                               reset_strbuf(&sbuf);
+                               goto out;
                        }
-                       fwd += r;
                        break;
                }
-               if (fwd > len)
-                       fwd = len;
-       } while (*f++);
-       return fwd;
+       } while (*fmt++);
+out:
+       return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
+                                  get_strbuf_len(&sbuf));
 }
 
 static const char quote_marker[] = { '\0', '"', '\0' };
index 06666ccfd8038a05fd6343b3e434f7fb26188403..b43d46f88ce0f229a6b0e02f331b0304f8c33114 100644 (file)
 /* local includes */
 #include "vector.h"
 #include "config.h"
+struct strbuf;
 
 /* Global definitions */
 #define EOB  "}"
 #define MAXBUF 1024
 
-/* ketword definition */
+
+/* keyword definition */
+typedef int print_fn(struct config *, struct strbuf *, const void *);
+
 struct keyword {
        char *string;
        int (*handler) (struct config *, vector);
-       int (*print) (struct config *, char *, int, const void *);
+       print_fn *print;
        vector sub;
        int unique;
 };
@@ -60,16 +64,15 @@ struct keyword {
 /* Prototypes */
 extern int keyword_alloc(vector keywords, char *string,
                         int (*handler) (struct config *, vector),
-                        int (*print) (struct config *, char *, int,
-                                      const void *),
+                        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),
-                           int (*print) (struct config *, char *, int,
-                                         const void *),
+                           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)
@@ -79,7 +82,7 @@ extern vector alloc_strvec(char *string);
 extern void *set_value(vector strvec);
 extern int process_file(struct config *conf, const char *conf_file);
 extern struct keyword * find_keyword(vector keywords, vector v, char * name);
-int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+int snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
                    const void *data);
 bool is_quote(const char* token);
 
index 8151e11e4186f8ed3d444d75f51b66f3ff388b9f..2fb9f4efdab8158ed4bcfc8d6df5ce5271595c70 100644 (file)
@@ -10,6 +10,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 #include <libudev.h>
 
 #include "checkers.h"
 #include "discovery.h"
 #include "util.h"
 #include "foreign.h"
+#include "strbuf.h"
+
+#define PRINT_PATH_LONG      "%w %i %d %D %p %t %T %s %o"
+#define PRINT_PATH_INDENT    "%i %d %D %t %T %o"
+#define PRINT_MAP_PROPS      "size=%S features='%f' hwhandler='%h' wp=%r"
+#define PRINT_PG_INDENT      "policy='%s' prio=%p status=%t"
+
+#define PRINT_JSON_MULTIPLIER     5
+#define PRINT_JSON_MAJOR_VERSION  0
+#define PRINT_JSON_MINOR_VERSION  1
+#define PRINT_JSON_START_VERSION  "   \"major_version\": %d,\n" \
+                                 "   \"minor_version\": %d,\n"
+#define PRINT_JSON_START_ELEM     "{\n"
+#define PRINT_JSON_START_MAP      "   \"map\":"
+#define PRINT_JSON_START_MAPS     "\"maps\": ["
+#define PRINT_JSON_START_PATHS    "\"paths\": ["
+#define PRINT_JSON_START_GROUPS   "\"path_groups\": ["
+#define PRINT_JSON_END_ELEM       "},"
+#define PRINT_JSON_END_LAST_ELEM  "}"
+#define PRINT_JSON_END_LAST       "}\n"
+#define PRINT_JSON_END_ARRAY      "]\n"
+#define PRINT_JSON_INDENT_N    3
+#define PRINT_JSON_MAP       "{\n" \
+                            "      \"name\" : \"%n\",\n" \
+                            "      \"uuid\" : \"%w\",\n" \
+                            "      \"sysfs\" : \"%d\",\n" \
+                            "      \"failback\" : \"%F\",\n" \
+                            "      \"queueing\" : \"%Q\",\n" \
+                            "      \"paths\" : %N,\n" \
+                            "      \"write_prot\" : \"%r\",\n" \
+                            "      \"dm_st\" : \"%t\",\n" \
+                            "      \"features\" : \"%f\",\n" \
+                            "      \"hwhandler\" : \"%h\",\n" \
+                            "      \"action\" : \"%A\",\n" \
+                            "      \"path_faults\" : %0,\n" \
+                            "      \"vend\" : \"%v\",\n" \
+                            "      \"prod\" : \"%p\",\n" \
+                            "      \"rev\" : \"%e\",\n" \
+                            "      \"switch_grp\" : %1,\n" \
+                            "      \"map_loads\" : %2,\n" \
+                            "      \"total_q_time\" : %3,\n" \
+                            "      \"q_timeouts\" : %4,"
+
+#define PRINT_JSON_GROUP     "{\n" \
+                            "         \"selector\" : \"%s\",\n" \
+                            "         \"pri\" : %p,\n" \
+                            "         \"dm_st\" : \"%t\",\n" \
+                            "         \"marginal_st\" : \"%M\","
+
+#define PRINT_JSON_GROUP_NUM "         \"group\" : %d,\n"
+
+#define PRINT_JSON_PATH      "{\n" \
+                            "            \"dev\" : \"%d\",\n"\
+                            "            \"dev_t\" : \"%D\",\n" \
+                            "            \"dm_st\" : \"%t\",\n" \
+                            "            \"dev_st\" : \"%o\",\n" \
+                            "            \"chk_st\" : \"%T\",\n" \
+                            "            \"checker\" : \"%c\",\n" \
+                            "            \"pri\" : %p,\n" \
+                            "            \"host_wwnn\" : \"%N\",\n" \
+                            "            \"target_wwnn\" : \"%n\",\n" \
+                            "            \"host_wwpn\" : \"%R\",\n" \
+                            "            \"target_wwpn\" : \"%r\",\n" \
+                            "            \"host_adapter\" : \"%a\",\n" \
+                            "            \"marginal_st\" : \"%M\""
+
+#define PROGRESS_LEN  10
 
 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
 #define MIN(x,y) (((x) > (y)) ? (y) : (x))
-#define TAIL     (line + len - 1 - c)
-#define NOPAD    s = c
-#define PAD(x) \
-do { \
-       while (c < (s + x) && (c < (line + len - 1))) \
-               *c++ = ' '; \
-       s = c; \
-} while (0)
-
-static char *
-__endline(char *line, size_t len, char *c)
-{
-       if (c > line) {
-               if (c >= line + len)
-                       c = line + len - 1;
-               *(c - 1) = '\n';
-               *c = '\0';
-       }
-       return c;
-}
-
-#define PRINT(var, size, format, args...) \
-do { \
-       fwd = snprintf(var, size, format, ##args); \
-       c += (fwd >= size) ? size : fwd; \
-} while (0)
-
 /*
  * information printing helpers
  */
 static int
-snprint_str (char * buff, size_t len, const char * str)
+snprint_str(struct strbuf *buff, const char *str)
 {
-       return snprintf(buff, len, "%s", str);
+       return append_strbuf_str(buff, str);
 }
 
 static int
-snprint_int (char * buff, size_t len, int val)
+snprint_int (struct strbuf *buff, int val)
 {
-       return snprintf(buff, len, "%i", val);
+       return print_strbuf(buff, "%i", val);
 }
 
 static int
-snprint_uint (char * buff, size_t len, unsigned int val)
+snprint_uint (struct strbuf *buff, unsigned int val)
 {
-       return snprintf(buff, len, "%u", val);
+       return print_strbuf(buff, "%u", val);
 }
 
 static int
-snprint_size (char * buff, size_t len, unsigned long long size)
+snprint_size (struct strbuf *buff, unsigned long long size)
 {
        float s = (float)(size >> 1); /* start with KB */
        char units[] = {'K','M','G','T','P'};
@@ -94,184 +135,177 @@ snprint_size (char * buff, size_t len, unsigned long long size)
                u++;
        }
 
-       return snprintf(buff, len, "%.*f%c", s < 10, s, *u);
+       return print_strbuf(buff, "%.*f%c", s < 10, s, *u);
 }
 
 /*
  * multipath info printing functions
  */
 static int
-snprint_name (char * buff, size_t len, const struct multipath * mpp)
+snprint_name (struct strbuf *buff, const struct multipath * mpp)
 {
        if (mpp->alias)
-               return snprintf(buff, len, "%s", mpp->alias);
+               return append_strbuf_str(buff, mpp->alias);
        else
-               return snprintf(buff, len, "%s", mpp->wwid);
+               return append_strbuf_str(buff, mpp->wwid);
 }
 
 static int
-snprint_sysfs (char * buff, size_t len, const struct multipath * mpp)
+snprint_sysfs (struct strbuf *buff, const struct multipath * mpp)
 {
        if (mpp->dmi)
-               return snprintf(buff, len, "dm-%i", mpp->dmi->minor);
+               return print_strbuf(buff, "dm-%i", mpp->dmi->minor);
        else
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
 }
 
 static int
-snprint_ro (char * buff, size_t len, const struct multipath * mpp)
+snprint_ro (struct strbuf *buff, const struct multipath * mpp)
 {
        if (!mpp->dmi)
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
        if (mpp->dmi->read_only)
-               return snprintf(buff, len, "ro");
+               return append_strbuf_str(buff, "ro");
        else
-               return snprintf(buff, len, "rw");
+               return append_strbuf_str(buff, "rw");
 }
 
 static int
-snprint_progress (char * buff, size_t len, int cur, int total)
+snprint_progress (struct strbuf *buff, int cur, int total)
 {
-       char * c = buff;
-       char * end = buff + len;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc;
 
        if (total > 0) {
                int i = PROGRESS_LEN * cur / total;
                int j = PROGRESS_LEN - i;
 
-               while (i-- > 0) {
-                       c += snprintf(c, len, "X");
-                       if ((len = (end - c)) <= 1) goto out;
-               }
-
-               while (j-- > 0) {
-                       c += snprintf(c, len,  ".");
-                       if ((len = (end - c)) <= 1) goto out;
+               if ((rc = fill_strbuf(buff, 'X', i)) < 0 ||
+                   (rc = fill_strbuf(buff, '.', j) < 0)) {
+                       truncate_strbuf(buff, initial_len);
+                       return rc;
                }
        }
 
-       c += snprintf(c, len, " %i/%i", cur, total);
-
-out:
-       buff[c - buff + 1] = '\0';
-       return (c - buff + 1);
+       if ((rc = print_strbuf(buff, " %i/%i", cur, total)) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_failback (char * buff, size_t len, const struct multipath * mpp)
+snprint_failback (struct strbuf *buff, const struct multipath * mpp)
 {
        if (mpp->pgfailback == -FAILBACK_IMMEDIATE)
-               return snprintf(buff, len, "immediate");
+               return append_strbuf_str(buff, "immediate");
        if (mpp->pgfailback == -FAILBACK_FOLLOWOVER)
-               return snprintf(buff, len, "followover");
+               return append_strbuf_str(buff, "followover");
 
        if (!mpp->failback_tick)
-               return snprintf(buff, len, "-");
+               return append_strbuf_str(buff, "-");
        else
-               return snprint_progress(buff, len, mpp->failback_tick,
+               return snprint_progress(buff, mpp->failback_tick,
                                        mpp->pgfailback);
 }
 
 static int
-snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
+snprint_queueing (struct strbuf *buff, const struct multipath * mpp)
 {
        if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
-               return snprintf(buff, len, "off");
+               return append_strbuf_str(buff, "off");
        else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE)
-               return snprintf(buff, len, "on");
+               return append_strbuf_str(buff, "on");
        else if (mpp->no_path_retry == NO_PATH_RETRY_UNDEF)
-               return snprintf(buff, len, "-");
+               return append_strbuf_str(buff, "-");
        else if (mpp->no_path_retry > 0) {
                if (mpp->retry_tick > 0)
 
-                       return snprintf(buff, len, "%i sec",
-                                       mpp->retry_tick);
+                       return print_strbuf(buff, "%i sec", mpp->retry_tick);
                else if (mpp->retry_tick == 0 && count_active_paths(mpp) > 0)
-                       return snprintf(buff, len, "%i chk",
-                                       mpp->no_path_retry);
+                       return print_strbuf(buff, "%i chk",
+                                           mpp->no_path_retry);
                else
-                       return snprintf(buff, len, "off");
+                       return append_strbuf_str(buff, "off");
        }
        return 0;
 }
 
 static int
-snprint_nb_paths (char * buff, size_t len, const struct multipath * mpp)
+snprint_nb_paths (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_int(buff, len, count_active_paths(mpp));
+       return snprint_int(buff, count_active_paths(mpp));
 }
 
 static int
-snprint_dm_map_state (char * buff, size_t len, const struct multipath * mpp)
+snprint_dm_map_state (struct strbuf *buff, const struct multipath * mpp)
 {
        if (mpp->dmi && mpp->dmi->suspended)
-               return snprintf(buff, len, "suspend");
+               return append_strbuf_str(buff, "suspend");
        else
-               return snprintf(buff, len, "active");
+               return append_strbuf_str(buff, "active");
 }
 
 static int
-snprint_multipath_size (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_size (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_size(buff, len, mpp->size);
+       return snprint_size(buff, mpp->size);
 }
 
 static int
-snprint_features (char * buff, size_t len, const struct multipath * mpp)
+snprint_features (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_str(buff, len, mpp->features);
+       return snprint_str(buff, mpp->features);
 }
 
 static int
-snprint_hwhandler (char * buff, size_t len, const struct multipath * mpp)
+snprint_hwhandler (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_str(buff, len, mpp->hwhandler);
+       return snprint_str(buff, mpp->hwhandler);
 }
 
 static int
-snprint_path_faults (char * buff, size_t len, const struct multipath * mpp)
+snprint_path_faults (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_path_failures);
+       return snprint_uint(buff, mpp->stat_path_failures);
 }
 
 static int
-snprint_switch_grp (char * buff, size_t len, const struct multipath * mpp)
+snprint_switch_grp (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_switchgroup);
+       return snprint_uint(buff, mpp->stat_switchgroup);
 }
 
 static int
-snprint_map_loads (char * buff, size_t len, const struct multipath * mpp)
+snprint_map_loads (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_map_loads);
+       return snprint_uint(buff, mpp->stat_map_loads);
 }
 
 static int
-snprint_total_q_time (char * buff, size_t len, const struct multipath * mpp)
+snprint_total_q_time (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_total_queueing_time);
+       return snprint_uint(buff, mpp->stat_total_queueing_time);
 }
 
 static int
-snprint_q_timeouts (char * buff, size_t len, const struct multipath * mpp)
+snprint_q_timeouts (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_queueing_timeouts);
+       return snprint_uint(buff, mpp->stat_queueing_timeouts);
 }
 
 static int
-snprint_map_failures (char * buff, size_t len, const struct multipath * mpp)
+snprint_map_failures (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_uint(buff, len, mpp->stat_map_failures);
+       return snprint_uint(buff, mpp->stat_map_failures);
 }
 
 static int
-snprint_multipath_uuid (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_uuid (struct strbuf *buff, const struct multipath * mpp)
 {
-       return snprint_str(buff, len, mpp->wwid);
+       return snprint_str(buff, mpp->wwid);
 }
 
 static int
-snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_vpr (struct strbuf *buff, const struct multipath * mpp)
 {
        struct pathgroup * pgp;
        struct path * pp;
@@ -280,16 +314,16 @@ snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp)
        vector_foreach_slot(mpp->pg, pgp, i) {
                vector_foreach_slot(pgp->paths, pp, j) {
                        if (strlen(pp->vendor_id) && strlen(pp->product_id))
-                               return snprintf(buff, len, "%s,%s",
-                                               pp->vendor_id, pp->product_id);
+                               return print_strbuf(buff, "%s,%s",
+                                                   pp->vendor_id, pp->product_id);
                }
        }
-       return snprintf(buff, len, "##,##");
+       return append_strbuf_str(buff, "##,##");
 }
 
 
 static int
-snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_vend (struct strbuf *buff, const struct multipath * mpp)
 {
        struct pathgroup * pgp;
        struct path * pp;
@@ -298,14 +332,14 @@ snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp)
        vector_foreach_slot(mpp->pg, pgp, i) {
                vector_foreach_slot(pgp->paths, pp, j) {
                        if (strlen(pp->vendor_id))
-                               return snprintf(buff, len, "%s", pp->vendor_id);
+                               return append_strbuf_str(buff, pp->vendor_id);
                }
        }
-       return snprintf(buff, len, "##");
+       return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_prod (struct strbuf *buff, const struct multipath * mpp)
 {
        struct pathgroup * pgp;
        struct path * pp;
@@ -314,14 +348,14 @@ snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp)
        vector_foreach_slot(mpp->pg, pgp, i) {
                vector_foreach_slot(pgp->paths, pp, j) {
                        if (strlen(pp->product_id))
-                               return snprintf(buff, len, "%s", pp->product_id);
+                               return append_strbuf_str(buff, pp->product_id);
                }
        }
-       return snprintf(buff, len, "##");
+       return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_rev (struct strbuf *buff, const struct multipath * mpp)
 {
        struct pathgroup * pgp;
        struct path * pp;
@@ -330,40 +364,40 @@ snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp)
        vector_foreach_slot(mpp->pg, pgp, i) {
                vector_foreach_slot(pgp->paths, pp, j) {
                        if (strlen(pp->rev))
-                               return snprintf(buff, len, "%s", pp->rev);
+                               return append_strbuf_str(buff, pp->rev);
                }
        }
-       return snprintf(buff, len, "##");
+       return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_foreign (char * buff, size_t len,
+snprint_multipath_foreign (struct strbuf *buff,
                           __attribute__((unused)) const struct multipath * pp)
 {
-       return snprintf(buff, len, "%s", "--");
+       return append_strbuf_str(buff, "--");
 }
 
 static int
-snprint_action (char * buff, size_t len, const struct multipath * mpp)
+snprint_action (struct strbuf *buff, const struct multipath * mpp)
 {
        switch (mpp->action) {
        case ACT_REJECT:
-               return snprint_str(buff, len, ACT_REJECT_STR);
+               return snprint_str(buff, ACT_REJECT_STR);
        case ACT_RENAME:
-               return snprint_str(buff, len, ACT_RENAME_STR);
+               return snprint_str(buff, ACT_RENAME_STR);
        case ACT_RELOAD:
-               return snprint_str(buff, len, ACT_RELOAD_STR);
+               return snprint_str(buff, ACT_RELOAD_STR);
        case ACT_CREATE:
-               return snprint_str(buff, len, ACT_CREATE_STR);
+               return snprint_str(buff, ACT_CREATE_STR);
        case ACT_SWITCHPG:
-               return snprint_str(buff, len, ACT_SWITCHPG_STR);
+               return snprint_str(buff, ACT_SWITCHPG_STR);
        default:
                return 0;
        }
 }
 
 static int
-snprint_multipath_vpd_data(char * buff, size_t len,
+snprint_multipath_vpd_data(struct strbuf *buff,
                           const struct multipath * mpp)
 {
        struct pathgroup * pgp;
@@ -373,26 +407,26 @@ snprint_multipath_vpd_data(char * buff, size_t len,
        vector_foreach_slot(mpp->pg, pgp, i)
                vector_foreach_slot(pgp->paths, pp, j)
                        if (pp->vpd_data)
-                               return snprintf(buff, len, "%s", pp->vpd_data);
-       return snprintf(buff, len, "[undef]");
+                               return append_strbuf_str(buff, pp->vpd_data);
+       return append_strbuf_str(buff, "[undef]");
 }
 
 /*
  * path info printing functions
  */
 static int
-snprint_path_uuid (char * buff, size_t len, const struct path * pp)
+snprint_path_uuid (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_str(buff, len, pp->wwid);
+       return snprint_str(buff, pp->wwid);
 }
 
 static int
-snprint_hcil (char * buff, size_t len, const struct path * pp)
+snprint_hcil (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || pp->sg_id.host_no < 0)
-               return snprintf(buff, len, "#:#:#:#");
+               return append_strbuf_str(buff, "#:#:#:#");
 
-       return snprintf(buff, len, "%i:%i:%i:%i",
+       return print_strbuf(buff, "%i:%i:%i:%" PRIu64,
                        pp->sg_id.host_no,
                        pp->sg_id.channel,
                        pp->sg_id.scsi_id,
@@ -400,159 +434,158 @@ snprint_hcil (char * buff, size_t len, const struct path * pp)
 }
 
 static int
-snprint_dev (char * buff, size_t len, const struct path * pp)
+snprint_dev (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || !strlen(pp->dev))
-               return snprintf(buff, len, "-");
+               return append_strbuf_str(buff, "-");
        else
-               return snprint_str(buff, len, pp->dev);
+               return snprint_str(buff, pp->dev);
 }
 
 static int
-snprint_dev_t (char * buff, size_t len, const struct path * pp)
+snprint_dev_t (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || !strlen(pp->dev))
-               return snprintf(buff, len, "#:#");
+               return append_strbuf_str(buff, "#:#");
        else
-               return snprint_str(buff, len, pp->dev_t);
+               return snprint_str(buff, pp->dev_t);
 }
 
 static int
-snprint_offline (char * buff, size_t len, const struct path * pp)
+snprint_offline (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || !pp->mpp)
-               return snprintf(buff, len, "unknown");
+               return append_strbuf_str(buff, "unknown");
        else if (pp->offline)
-               return snprintf(buff, len, "offline");
+               return append_strbuf_str(buff, "offline");
        else
-               return snprintf(buff, len, "running");
+               return append_strbuf_str(buff, "running");
 }
 
 static int
-snprint_chk_state (char * buff, size_t len, const struct path * pp)
+snprint_chk_state (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || !pp->mpp)
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
 
        switch (pp->state) {
        case PATH_UP:
-               return snprintf(buff, len, "ready");
+               return append_strbuf_str(buff, "ready");
        case PATH_DOWN:
-               return snprintf(buff, len, "faulty");
+               return append_strbuf_str(buff, "faulty");
        case PATH_SHAKY:
-               return snprintf(buff, len, "shaky");
+               return append_strbuf_str(buff, "shaky");
        case PATH_GHOST:
-               return snprintf(buff, len, "ghost");
+               return append_strbuf_str(buff, "ghost");
        case PATH_PENDING:
-               return snprintf(buff, len, "i/o pending");
+               return append_strbuf_str(buff, "i/o pending");
        case PATH_TIMEOUT:
-               return snprintf(buff, len, "i/o timeout");
+               return append_strbuf_str(buff, "i/o timeout");
        case PATH_DELAYED:
-               return snprintf(buff, len, "delayed");
+               return append_strbuf_str(buff, "delayed");
        default:
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
        }
 }
 
 static int
-snprint_dm_path_state (char * buff, size_t len, const struct path * pp)
+snprint_dm_path_state (struct strbuf *buff, const struct path * pp)
 {
        if (!pp)
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
 
        switch (pp->dmstate) {
        case PSTATE_ACTIVE:
-               return snprintf(buff, len, "active");
+               return append_strbuf_str(buff, "active");
        case PSTATE_FAILED:
-               return snprintf(buff, len, "failed");
+               return append_strbuf_str(buff, "failed");
        default:
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
        }
 }
 
 static int
-snprint_vpr (char * buff, size_t len, const struct path * pp)
+snprint_vpr (struct strbuf *buff, const struct path * pp)
 {
-       return snprintf(buff, len, "%s,%s",
-                       pp->vendor_id, pp->product_id);
+       return print_strbuf(buff, "%s,%s", pp->vendor_id, pp->product_id);
 }
 
 static int
-snprint_next_check (char * buff, size_t len, const struct path * pp)
+snprint_next_check (struct strbuf *buff, const struct path * pp)
 {
        if (!pp || !pp->mpp)
-               return snprintf(buff, len, "orphan");
+               return append_strbuf_str(buff, "orphan");
 
-       return snprint_progress(buff, len, pp->tick, pp->checkint);
+       return snprint_progress(buff, pp->tick, pp->checkint);
 }
 
 static int
-snprint_pri (char * buff, size_t len, const struct path * pp)
+snprint_pri (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_int(buff, len, pp ? pp->priority : -1);
+       return snprint_int(buff, pp ? pp->priority : -1);
 }
 
 static int
-snprint_pg_selector (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_selector (struct strbuf *buff, const struct pathgroup * pgp)
 {
        const char *s = pgp->mpp->selector;
 
-       return snprint_str(buff, len, s ? s : "");
+       return snprint_str(buff, s ? s : "");
 }
 
 static int
-snprint_pg_pri (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_pri (struct strbuf *buff, const struct pathgroup * pgp)
 {
-       return snprint_int(buff, len, pgp->priority);
+       return snprint_int(buff, pgp->priority);
 }
 
 static int
-snprint_pg_state (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_state (struct strbuf *buff, const struct pathgroup * pgp)
 {
        switch (pgp->status) {
        case PGSTATE_ENABLED:
-               return snprintf(buff, len, "enabled");
+               return append_strbuf_str(buff, "enabled");
        case PGSTATE_DISABLED:
-               return snprintf(buff, len, "disabled");
+               return append_strbuf_str(buff, "disabled");
        case PGSTATE_ACTIVE:
-               return snprintf(buff, len, "active");
+               return append_strbuf_str(buff, "active");
        default:
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
        }
 }
 
 static int
-snprint_pg_marginal (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_marginal (struct strbuf *buff, const struct pathgroup * pgp)
 {
        if (pgp->marginal)
-               return snprintf(buff, len, "marginal");
-       return snprintf(buff, len, "normal");
+               return append_strbuf_str(buff, "marginal");
+       return append_strbuf_str(buff, "normal");
 }
 
 static int
-snprint_path_size (char * buff, size_t len, const struct path * pp)
+snprint_path_size (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_size(buff, len, pp->size);
+       return snprint_size(buff, pp->size);
 }
 
 int
-snprint_path_serial (char * buff, size_t len, const struct path * pp)
+snprint_path_serial (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_str(buff, len, pp->serial);
+       return snprint_str(buff, pp->serial);
 }
 
 static int
-snprint_path_mpp (char * buff, size_t len, const struct path * pp)
+snprint_path_mpp (struct strbuf *buff, const struct path * pp)
 {
        if (!pp->mpp)
-               return snprintf(buff, len, "[orphan]");
+               return append_strbuf_str(buff, "[orphan]");
        if (!pp->mpp->alias)
-               return snprintf(buff, len, "[unknown]");
-       return snprint_str(buff, len, pp->mpp->alias);
+               return append_strbuf_str(buff, "[unknown]");
+       return snprint_str(buff, pp->mpp->alias);
 }
 
 static int
-snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
+snprint_host_attr (struct strbuf *buff, const struct path * pp, char *attr)
 {
        struct udev_device *host_dev = NULL;
        char host_id[32];
@@ -560,7 +593,7 @@ snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
        int ret;
 
        if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
-               return snprintf(buff, len, "[undef]");
+               return append_strbuf_str(buff, "[undef]");
        sprintf(host_id, "host%d", pp->sg_id.host_no);
        host_dev = udev_device_new_from_subsystem_sysname(udev, "fc_host",
                                                          host_id);
@@ -570,36 +603,36 @@ snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
        }
        value = udev_device_get_sysattr_value(host_dev, attr);
        if (value)
-               ret = snprint_str(buff, len, value);
+               ret = snprint_str(buff, value);
        udev_device_unref(host_dev);
 out:
        if (!value)
-               ret = snprintf(buff, len, "[unknown]");
+               ret = append_strbuf_str(buff, "[unknown]");
        return ret;
 }
 
 int
-snprint_host_wwnn (char * buff, size_t len, const struct path * pp)
+snprint_host_wwnn (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_host_attr(buff, len, pp, "node_name");
+       return snprint_host_attr(buff, pp, "node_name");
 }
 
 int
-snprint_host_wwpn (char * buff, size_t len, const struct path * pp)
+snprint_host_wwpn (struct strbuf *buff, const struct path * pp)
 {
-       return snprint_host_attr(buff, len, pp, "port_name");
+       return snprint_host_attr(buff, pp, "port_name");
 }
 
 int
-snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp)
+snprint_tgt_wwpn (struct strbuf *buff, const struct path * pp)
 {
        struct udev_device *rport_dev = NULL;
-       char rport_id[32];
+       char rport_id[42];
        const char *value = NULL;
        int ret;
 
        if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
-               return snprintf(buff, len, "[undef]");
+               return append_strbuf_str(buff, "[undef]");
        sprintf(rport_id, "rport-%d:%d-%d",
                pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
        rport_dev = udev_device_new_from_subsystem_sysname(udev,
@@ -611,111 +644,111 @@ snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp)
        }
        value = udev_device_get_sysattr_value(rport_dev, "port_name");
        if (value)
-               ret = snprint_str(buff, len, value);
+               ret = snprint_str(buff, value);
        udev_device_unref(rport_dev);
 out:
        if (!value)
-               ret = snprintf(buff, len, "[unknown]");
+               ret = append_strbuf_str(buff, "[unknown]");
        return ret;
 }
 
 
 int
-snprint_tgt_wwnn (char * buff, size_t len, const struct path * pp)
+snprint_tgt_wwnn (struct strbuf *buff, const struct path * pp)
 {
        if (pp->tgt_node_name[0] == '\0')
-               return snprintf(buff, len, "[undef]");
-       return snprint_str(buff, len, pp->tgt_node_name);
+               return append_strbuf_str(buff, "[undef]");
+       return snprint_str(buff, pp->tgt_node_name);
 }
 
 static int
-snprint_host_adapter (char * buff, size_t len, const struct path * pp)
+snprint_host_adapter (struct strbuf *buff, const struct path * pp)
 {
        char adapter[SLOT_NAME_SIZE];
 
        if (sysfs_get_host_adapter_name(pp, adapter))
-               return snprintf(buff, len, "[undef]");
-       return snprint_str(buff, len, adapter);
+               return append_strbuf_str(buff, "[undef]");
+       return snprint_str(buff, adapter);
 }
 
 static int
-snprint_path_checker (char * buff, size_t len, const struct path * pp)
+snprint_path_checker (struct strbuf *buff, const struct path * pp)
 {
        const struct checker * c = &pp->checker;
-       return snprint_str(buff, len, checker_name(c));
+       return snprint_str(buff, checker_name(c));
 }
 
 static int
-snprint_path_foreign (char * buff, size_t len,
+snprint_path_foreign (struct strbuf *buff,
                      __attribute__((unused)) const struct path * pp)
 {
-       return snprintf(buff, len, "%s", "--");
+       return append_strbuf_str(buff, "--");
 }
 
 static int
-snprint_path_failures(char * buff, size_t len, const struct path * pp)
+snprint_path_failures(struct strbuf *buff, const struct path * pp)
 {
-       return snprint_int(buff, len, pp->failcount);
+       return snprint_int(buff, pp->failcount);
 }
 
 /* if you add a protocol string bigger than "scsi:unspec" you must
  * also change PROTOCOL_BUF_SIZE */
 int
-snprint_path_protocol(char * buff, size_t len, const struct path * pp)
+snprint_path_protocol(struct strbuf *buff, const struct path * pp)
 {
        switch (pp->bus) {
        case SYSFS_BUS_SCSI:
                switch (pp->sg_id.proto_id) {
                case SCSI_PROTOCOL_FCP:
-                       return snprintf(buff, len, "scsi:fcp");
+                       return append_strbuf_str(buff, "scsi:fcp");
                case SCSI_PROTOCOL_SPI:
-                       return snprintf(buff, len, "scsi:spi");
+                       return append_strbuf_str(buff, "scsi:spi");
                case SCSI_PROTOCOL_SSA:
-                       return snprintf(buff, len, "scsi:ssa");
+                       return append_strbuf_str(buff, "scsi:ssa");
                case SCSI_PROTOCOL_SBP:
-                       return snprintf(buff, len, "scsi:sbp");
+                       return append_strbuf_str(buff, "scsi:sbp");
                case SCSI_PROTOCOL_SRP:
-                       return snprintf(buff, len, "scsi:srp");
+                       return append_strbuf_str(buff, "scsi:srp");
                case SCSI_PROTOCOL_ISCSI:
-                       return snprintf(buff, len, "scsi:iscsi");
+                       return append_strbuf_str(buff, "scsi:iscsi");
                case SCSI_PROTOCOL_SAS:
-                       return snprintf(buff, len, "scsi:sas");
+                       return append_strbuf_str(buff, "scsi:sas");
                case SCSI_PROTOCOL_ADT:
-                       return snprintf(buff, len, "scsi:adt");
+                       return append_strbuf_str(buff, "scsi:adt");
                case SCSI_PROTOCOL_ATA:
-                       return snprintf(buff, len, "scsi:ata");
+                       return append_strbuf_str(buff, "scsi:ata");
                case SCSI_PROTOCOL_USB:
-                       return snprintf(buff, len, "scsi:usb");
+                       return append_strbuf_str(buff, "scsi:usb");
                case SCSI_PROTOCOL_UNSPEC:
                default:
-                       return snprintf(buff, len, "scsi:unspec");
+                       return append_strbuf_str(buff, "scsi:unspec");
                }
        case SYSFS_BUS_CCW:
-               return snprintf(buff, len, "ccw");
+               return append_strbuf_str(buff, "ccw");
        case SYSFS_BUS_CCISS:
-               return snprintf(buff, len, "cciss");
+               return append_strbuf_str(buff, "cciss");
        case SYSFS_BUS_NVME:
-               return snprintf(buff, len, "nvme");
+               return append_strbuf_str(buff, "nvme");
        case SYSFS_BUS_UNDEF:
        default:
-               return snprintf(buff, len, "undef");
+               return append_strbuf_str(buff, "undef");
        }
 }
 
 int
-snprint_path_marginal(char * buff, size_t len, const struct path * pp)
+snprint_path_marginal(struct strbuf *buff, const struct path * pp)
 {
        if (pp->marginal)
-               return snprintf(buff, len, "marginal");
-       return snprintf(buff, len, "normal");
+               return append_strbuf_str(buff, "marginal");
+       return append_strbuf_str(buff, "normal");
 }
 
 static int
-snprint_path_vpd_data(char * buff, size_t len, const struct path * pp)
+snprint_path_vpd_data(struct strbuf *buff, const struct path * pp)
 {
        if (pp->vpd_data)
-               return snprintf(buff, len, "%s", pp->vpd_data);
-       return snprintf(buff, len, "[undef]");
+               return append_strbuf_str(buff, pp->vpd_data);
+       return append_strbuf_str(buff, "[undef]");
 }
 
 struct multipath_data mpd[] = {
@@ -782,24 +815,33 @@ struct pathgroup_data pgd[] = {
        {0, NULL, 0 , NULL}
 };
 
-int
-snprint_wildcards (char * buff, int len)
+int snprint_wildcards(struct strbuf *buff)
 {
-       int i, fwd = 0;
+       int initial_len = get_strbuf_len(buff);
+       int i, rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "multipath format wildcards:\n");
+       if ((rc = append_strbuf_str(buff, "multipath format wildcards:\n")) < 0)
+               return rc;
        for (i = 0; mpd[i].header; i++)
-               fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-                               mpd[i].wildcard, mpd[i].header);
-       fwd += snprintf(buff + fwd, len - fwd, "\npath format wildcards:\n");
+               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++)
-               fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-                               pd[i].wildcard, pd[i].header);
-       fwd += snprintf(buff + fwd, len - fwd, "\npathgroup format wildcards:\n");
+               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++)
-               fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-                               pgd[i].wildcard, pgd[i].header);
-       return fwd;
+               if ((rc = print_strbuf(buff, "%%%c  %s\n",
+                                      pgd[i].wildcard, pgd[i].header)) < 0)
+                       return rc;
+
+       return get_strbuf_len(buff) - initial_len;
 }
 
 void
@@ -832,10 +874,10 @@ void
 _get_path_layout (const struct _vector *gpvec, enum layout_reset reset)
 {
        int i, j;
-       char buff[MAX_FIELD_LEN];
        const struct gen_path *gp;
 
        for (j = 0; pd[j].header; j++) {
+               STRBUF_ON_STACK(buff);
 
                reset_width(&pd[j].width, reset, pd[j].header);
 
@@ -843,9 +885,9 @@ _get_path_layout (const struct _vector *gpvec, enum layout_reset reset)
                        continue;
 
                vector_foreach_slot (gpvec, gp, i) {
-                       gp->ops->snprint(gp, buff, MAX_FIELD_LEN,
-                                        pd[j].wildcard);
-                       pd[j].width = MAX(pd[j].width, strlen(buff));
+                       gp->ops->snprint(gp, &buff, pd[j].wildcard);
+                       pd[j].width = MAX(pd[j].width, get_strbuf_len(&buff));
+                       truncate_strbuf(&buff, 0);
                }
        }
 }
@@ -873,10 +915,10 @@ _get_multipath_layout (const struct _vector *gmvec,
                            enum layout_reset reset)
 {
        int i, j;
-       char buff[MAX_FIELD_LEN];
        const struct gen_multipath * gm;
 
        for (j = 0; mpd[j].header; j++) {
+               STRBUF_ON_STACK(buff);
 
                reset_width(&mpd[j].width, reset, mpd[j].header);
 
@@ -884,9 +926,9 @@ _get_multipath_layout (const struct _vector *gmvec,
                        continue;
 
                vector_foreach_slot (gmvec, gm, i) {
-                       gm->ops->snprint(gm, buff, MAX_FIELD_LEN,
-                                        mpd[j].wildcard);
-                       mpd[j].width = MAX(mpd[j].width, strlen(buff));
+                       gm->ops->snprint(gm, &buff, mpd[j].wildcard);
+                       mpd[j].width = MAX(mpd[j].width, get_strbuf_len(&buff));
+                       truncate_strbuf(&buff, 0);
                }
                condlog(4, "%s: width %d", mpd[j].header, mpd[j].width);
        }
@@ -905,14 +947,14 @@ mpd_lookup(char wildcard)
 }
 
 int snprint_multipath_attr(const struct gen_multipath* gm,
-                          char *buf, int len, char wildcard)
+                          struct strbuf *buf, char wildcard)
 {
        const struct multipath *mpp = gen_multipath_to_dm(gm);
        struct multipath_data *mpd = mpd_lookup(wildcard);
 
        if (mpd == NULL)
                return 0;
-       return mpd->snprint(buf, len, mpp);
+       return mpd->snprint(buf, mpp);
 }
 
 static struct path_data *
@@ -928,14 +970,14 @@ pd_lookup(char wildcard)
 }
 
 int snprint_path_attr(const struct gen_path* gp,
-                     char *buf, int len, char wildcard)
+                     struct strbuf *buf, char wildcard)
 {
        const struct path *pp = gen_path_to_dm(gp);
        struct path_data *pd = pd_lookup(wildcard);
 
        if (pd == NULL)
                return 0;
-       return pd->snprint(buf, len, pp);
+       return pd->snprint(buf, pp);
 }
 
 static struct pathgroup_data *
@@ -951,220 +993,168 @@ pgd_lookup(char wildcard)
 }
 
 int snprint_pathgroup_attr(const struct gen_pathgroup* gpg,
-                          char *buf, int len, char wildcard)
+                          struct strbuf *buf, char wildcard)
 {
        const struct pathgroup *pg = gen_pathgroup_to_dm(gpg);
        struct pathgroup_data *pdg = pgd_lookup(wildcard);
 
        if (pdg == NULL)
                return 0;
-       return pdg->snprint(buf, len, pg);
+       return pdg->snprint(buf, pg);
 }
 
-int
-snprint_multipath_header (char * line, int len, const char * format)
+int snprint_multipath_header(struct strbuf *line, const char *format)
 {
-       char * c = line;   /* line cursor */
-       char * s = line;   /* for padding */
-       const char * f = format; /* format string cursor */
-       int fwd;
+       int initial_len = get_strbuf_len(line);
+       const char *f;
        struct multipath_data * data;
+       int rc;
 
-       do {
-               if (TAIL <= 0)
-                       break;
-
-               if (*f != '%') {
-                       *c++ = *f;
-                       NOPAD;
-                       continue;
-               }
-               f++;
+       for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+               if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+                       return rc;
 
-               if (!(data = mpd_lookup(*f)))
+               format = f + 1;
+               if (!(data = mpd_lookup(*format)))
                        continue; /* unknown wildcard */
 
-               PRINT(c, TAIL, "%s", data->header);
-               PAD(data->width);
-       } while (*f++);
+               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)
+                               return rc;
+       }
 
-       __endline(line, len, c);
-       return (c - line);
+       if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+               return rc;
+       return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_multipath (const struct gen_multipath * gmp,
-                   char * line, int len, const char * format, int pad)
+int _snprint_multipath(const struct gen_multipath *gmp,
+                      struct strbuf *line, const char *format, int pad)
 {
-       char * c = line;   /* line cursor */
-       char * s = line;   /* for padding */
-       const char * f = format; /* format string cursor */
-       int fwd;
+       int initial_len = get_strbuf_len(line);
+       const char *f;
        struct multipath_data * data;
-       char buff[MAX_FIELD_LEN] = {};
-
-       do {
-               if (TAIL <= 0)
-                       break;
+       int rc;
 
-               if (*f != '%') {
-                       *c++ = *f;
-                       NOPAD;
-                       continue;
-               }
-               f++;
+       for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+               if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+                       return rc;
 
-               if (!(data = mpd_lookup(*f)))
-                       continue;
+               format = f + 1;
+               if (!(data = mpd_lookup(*format)))
+                       continue; /* unknown wildcard */
 
-               gmp->ops->snprint(gmp, buff, MAX_FIELD_LEN, *f);
-               PRINT(c, TAIL, "%s", buff);
-               if (pad)
-                       PAD(data->width);
-               buff[0] = '\0';
-       } while (*f++);
+               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)
+                               return rc;
+       }
 
-       __endline(line, len, c);
-       return (c - line);
+       if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+               return rc;
+       return get_strbuf_len(line) - initial_len;
 }
 
-int
-snprint_path_header (char * line, int len, const char * format)
+int snprint_path_header(struct strbuf *line, const char *format)
 {
-       char * c = line;   /* line cursor */
-       char * s = line;   /* for padding */
-       const char * f = format; /* format string cursor */
-       int fwd;
-       struct path_data * data;
+       int initial_len = get_strbuf_len(line);
+       const char *f;
+       struct path_data *data;
+       int rc;
 
-       do {
-               if (TAIL <= 0)
-                       break;
+       for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+               if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+                       return rc;
 
-               if (*f != '%') {
-                       *c++ = *f;
-                       NOPAD;
-                       continue;
-               }
-               f++;
-
-               if (!(data = pd_lookup(*f)))
+               format = f + 1;
+               if (!(data = pd_lookup(*format)))
                        continue; /* unknown wildcard */
 
-               PRINT(c, TAIL, "%s", data->header);
-               PAD(data->width);
-       } while (*f++);
+               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)
+                               return rc;
+       }
 
-       __endline(line, len, c);
-       return (c - line);
+       if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+               return rc;
+       return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_path (const struct gen_path * gp, char * line, int len,
-              const char * format, int pad)
+int _snprint_path(const struct gen_path *gp, struct strbuf *line,
+                 const char *format, int pad)
 {
-       char * c = line;   /* line cursor */
-       char * s = line;   /* for padding */
-       const char * f = format; /* format string cursor */
-       int fwd;
+       int initial_len = get_strbuf_len(line);
+       const char *f;
        struct path_data * data;
-       char buff[MAX_FIELD_LEN];
+       int rc;
 
-       do {
-               if (TAIL <= 0)
-                       break;
-
-               if (*f != '%') {
-                       *c++ = *f;
-                       NOPAD;
-                       continue;
-               }
-               f++;
+       for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+               if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+                       return rc;
 
-               if (!(data = pd_lookup(*f)))
-                       continue;
+               format = f + 1;
+               if (!(data = pd_lookup(*format)))
+                       continue; /* unknown wildcard */
 
-               gp->ops->snprint(gp, buff, MAX_FIELD_LEN, *f);
-               PRINT(c, TAIL, "%s", buff);
-               if (pad)
-                       PAD(data->width);
-       } while (*f++);
+               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)
+                               return rc;
+       }
 
-       __endline(line, len, c);
-       return (c - line);
+       if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+               return rc;
+       return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_pathgroup (const struct gen_pathgroup * ggp, char * line, int len,
-                   char * format)
-{
-       char * c = line;   /* line cursor */
-       char * s = line;   /* for padding */
-       char * f = format; /* format string cursor */
-       int fwd;
-       struct pathgroup_data * data;
-       char buff[MAX_FIELD_LEN];
-
-       do {
-               if (TAIL <= 0)
-                       break;
+int _snprint_pathgroup(const struct gen_pathgroup *ggp, struct strbuf *line,
+                      const char *format)
+{
+       int initial_len = get_strbuf_len(line);
+       const char *f;
+       struct pathgroup_data *data;
+       int rc;
 
-               if (*f != '%') {
-                       *c++ = *f;
-                       NOPAD;
-                       continue;
-               }
-               f++;
+       for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+               if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+                       return rc;
 
-               if (!(data = pgd_lookup(*f)))
-                       continue;
+               format = f + 1;
+               if (!(data = pgd_lookup(*format)))
+                       continue; /* unknown wildcard */
 
-               ggp->ops->snprint(ggp, buff, MAX_FIELD_LEN, *f);
-               PRINT(c, TAIL, "%s", buff);
-               PAD(data->width);
-       } while (*f++);
+               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;
+       }
 
-       __endline(line, len, c);
-       return (c - line);
+       if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+               return rc;
+       return get_strbuf_len(line) - initial_len;
 }
-#define snprint_pathgroup(line, len, fmt, pgp) \
-       _snprint_pathgroup(dm_pathgroup_to_gen(pgp), line, len, fmt)
+
+#define snprint_pathgroup(line, fmt, pgp)                              \
+       _snprint_pathgroup(dm_pathgroup_to_gen(pgp), line, fmt)
 
 void _print_multipath_topology(const struct gen_multipath *gmp, int verbosity)
 {
-       int resize;
-       char *buff = NULL;
-       char *old = NULL;
-       int len, maxlen = MAX_LINE_LEN * MAX_LINES;
-
-       buff = MALLOC(maxlen);
-       do {
-               if (!buff) {
-                       if (old)
-                               FREE(old);
-                       condlog(0, "couldn't allocate memory for list: %s\n",
-                               strerror(errno));
-                       return;
-               }
+       STRBUF_ON_STACK(buff);
 
-               len = _snprint_multipath_topology(gmp, buff, maxlen, verbosity);
-               resize = (len == maxlen - 1);
-
-               if (resize) {
-                       maxlen *= 2;
-                       old = buff;
-                       buff = REALLOC(buff, maxlen);
-               }
-       } while (resize);
-       printf("%s", buff);
-       FREE(buff);
+       _snprint_multipath_topology(gmp, &buff, verbosity);
+       printf("%s", get_strbuf_str(&buff));
 }
 
-int
-snprint_multipath_style(const struct gen_multipath *gmp, char *style, int len,
-                       int verbosity)
+int snprint_multipath_style(const struct gen_multipath *gmp,
+                           struct strbuf *style, int verbosity)
 {
-       int n;
        const struct multipath *mpp = gen_multipath_to_dm(gmp);
        bool need_action = (verbosity > 1 &&
                            mpp->action != ACT_NOTHING &&
@@ -1172,381 +1162,299 @@ snprint_multipath_style(const struct gen_multipath *gmp, char *style, int len,
                            mpp->action != ACT_IMPOSSIBLE);
        bool need_wwid = (strncmp(mpp->alias, mpp->wwid, WWID_SIZE));
 
-       n = snprintf(style, len, "%s%s%s%s",
-                    need_action ? "%A: " : "", "%n",
-                    need_wwid ? " (%w)" : "", " %d %s");
-       return MIN(n, len - 1);
+       return print_strbuf(style, "%s%s%s%s",
+                           need_action ? "%A: " : "", "%n",
+                           need_wwid ? " (%w)" : "", " %d %s");
 }
 
 int _snprint_multipath_topology(const struct gen_multipath *gmp,
-                               char *buff, int len, int verbosity)
+                               struct strbuf *buff, int verbosity)
 {
-       int j, i, fwd = 0;
+       int j, i, rc;
        const struct _vector *pgvec;
        const struct gen_pathgroup *gpg;
-       char style[64];
-       char * c = style;
-       char fmt[64];
-       char * f;
+       STRBUF_ON_STACK(style);
+       size_t initial_len = get_strbuf_len(buff);
 
        if (verbosity <= 0)
-               return fwd;
+               return 0;
 
        reset_multipath_layout();
 
        if (verbosity == 1)
-               return _snprint_multipath(gmp, buff, len, "%n", 1);
-
-       if(isatty(1))
-               c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */
+               return _snprint_multipath(gmp, buff, "%n", 1);
 
-       c += gmp->ops->style(gmp, c, sizeof(style) - (c - style),
-                            verbosity);
-       if(isatty(1))
-               c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */
+       if(isatty(1) &&
+          (rc = print_strbuf(&style, "%c[%dm", 0x1B, 1)) < 0) /* bold on */
+               return rc;
+       if ((rc = gmp->ops->style(gmp, &style, verbosity)) < 0)
+               return rc;
+       if(isatty(1) &&
+          (rc = print_strbuf(&style, "%c[%dm", 0x1B, 0)) < 0) /* bold off */
+               return rc;
 
-       fwd += _snprint_multipath(gmp, buff + fwd, len - fwd, style, 1);
-       if (fwd >= len)
-               return len;
-       fwd += _snprint_multipath(gmp, buff + fwd, len - fwd,
-                                 PRINT_MAP_PROPS, 1);
-       if (fwd >= len)
-               return len;
+       if ((rc = _snprint_multipath(gmp, buff, get_strbuf_str(&style), 1)) < 0
+           || (rc = _snprint_multipath(gmp, buff, PRINT_MAP_PROPS, 1)) < 0)
+               return rc;
 
        pgvec = gmp->ops->get_pathgroups(gmp);
        if (pgvec == NULL)
-               return fwd;
+               goto out;
 
        vector_foreach_slot (pgvec, gpg, j) {
                const struct _vector *pathvec;
                struct gen_path *gp;
+               bool last_group = j + 1 == VECTOR_SIZE(pgvec);
 
-               f=fmt;
-
-               if (j + 1 < VECTOR_SIZE(pgvec)) {
-                       strcpy(f, "|-+- " PRINT_PG_INDENT);
-               } else
-                       strcpy(f, "`-+- " PRINT_PG_INDENT);
-               fwd += _snprint_pathgroup(gpg, buff + fwd, len - fwd, fmt);
-
-               if (fwd >= len) {
-                       fwd = len;
-                       break;
-               }
+               if ((rc = print_strbuf(buff, "%c-+- ",
+                                      last_group ? '`' : '|')) < 0 ||
+                   (rc = _snprint_pathgroup(gpg, buff, PRINT_PG_INDENT)) < 0)
+                       return rc;
 
                pathvec = gpg->ops->get_paths(gpg);
                if (pathvec == NULL)
                        continue;
 
                vector_foreach_slot (pathvec, gp, i) {
-                       f=fmt;
-                       if (*f != '|')
-                               *f=' ';
-                       f++;
-                       if (i + 1 < VECTOR_SIZE(pathvec))
-                               strcpy(f, " |- " PRINT_PATH_INDENT);
-                       else
-                               strcpy(f, " `- " PRINT_PATH_INDENT);
-                       fwd += _snprint_path(gp, buff + fwd, len - fwd, fmt, 1);
-                       if (fwd >= len) {
-                               fwd = len;
-                               break;
-                       }
+                       if ((rc = print_strbuf(buff, "%c %c- ",
+                                              last_group ? ' ' : '|',
+                                              i + 1 == VECTOR_SIZE(pathvec) ?
+                                              '`': '|')) < 0 ||
+                           (rc = _snprint_path(gp, buff,
+                                               PRINT_PATH_INDENT, 1)) < 0)
+                               return rc;
                }
                gpg->ops->rel_paths(gpg, pathvec);
-
-               if (fwd == len)
-                       break;
        }
+
        gmp->ops->rel_pathgroups(gmp, pgvec);
-       return fwd;
+out:
+       return get_strbuf_len(buff) - initial_len;
 }
 
 
 static int
-snprint_json (char * buff, int len, int indent, char *json_str)
+snprint_json(struct strbuf *buff, int indent, const char *json_str)
 {
-       int fwd = 0, i;
+       int rc;
 
-       for (i = 0; i < indent; i++) {
-               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
-               if (fwd >= len)
-                       return fwd;
-       }
+       if ((rc = fill_strbuf(buff, ' ', indent * PRINT_JSON_INDENT_N)) < 0)
+               return rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "%s", json_str);
-       return fwd;
+       return append_strbuf_str(buff, json_str);
 }
 
-static int
-snprint_json_header (char * buff, int len)
+static int snprint_json_header(struct strbuf *buff)
 {
-       int fwd = 0;
-
-       fwd +=  snprint_json(buff, len, 0, PRINT_JSON_START_ELEM);
-       if (fwd >= len)
-               return fwd;
+       int rc;
 
-       fwd +=  snprintf(buff + fwd, len  - fwd, PRINT_JSON_START_VERSION,
-                       PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION);
-       return fwd;
+       if ((rc = snprint_json(buff, 0, PRINT_JSON_START_ELEM)) < 0)
+               return rc;
+       return print_strbuf(buff, PRINT_JSON_START_VERSION,
+                           PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION);
 }
 
-static int
-snprint_json_elem_footer (char * buff, int len, int indent, int last)
+static int snprint_json_elem_footer(struct strbuf *buff, int indent, bool last)
 {
-       int fwd = 0, i;
+       int rc;
 
-       for (i = 0; i < indent; i++) {
-               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
-               if (fwd >= len)
-                       return fwd;
-       }
+       if ((rc = fill_strbuf(buff, ' ', indent * PRINT_JSON_INDENT_N)) < 0)
+               return rc;
 
-       if (last == 1)
-               fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_LAST_ELEM);
+       if (last)
+               return append_strbuf_str(buff, PRINT_JSON_END_LAST_ELEM);
        else
-               fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_ELEM);
-       return fwd;
+               return append_strbuf_str(buff, PRINT_JSON_END_ELEM);
 }
 
-static int
-snprint_multipath_fields_json (char * buff, int len,
-               const struct multipath * mpp, int last)
+static int snprint_multipath_fields_json(struct strbuf *buff,
+                                        const struct multipath *mpp, int last)
 {
-       int i, j, fwd = 0;
+       int i, j, rc;
        struct path *pp;
        struct pathgroup *pgp;
+       size_t initial_len = get_strbuf_len(buff);
 
-       fwd += snprint_multipath(buff, len, PRINT_JSON_MAP, mpp, 0);
-       if (fwd >= len)
-               return fwd;
-
-       fwd += snprint_json(buff + fwd, len - fwd, 2, PRINT_JSON_START_GROUPS);
-       if (fwd >= len)
-               return fwd;
+       if ((rc = snprint_multipath(buff, PRINT_JSON_MAP, mpp, 0)) < 0 ||
+           (rc = snprint_json(buff, 2, PRINT_JSON_START_GROUPS)) < 0)
+               return rc;
 
        vector_foreach_slot (mpp->pg, pgp, i) {
 
-               fwd += snprint_pathgroup(buff + fwd, len - fwd, PRINT_JSON_GROUP, pgp);
-               if (fwd >= len)
-                       return fwd;
-
-               fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_GROUP_NUM, i + 1);
-               if (fwd >= len)
-                       return fwd;
-
-               fwd += snprint_json(buff + fwd, len - fwd, 3, PRINT_JSON_START_PATHS);
-               if (fwd >= len)
-                       return fwd;
+               if ((rc = snprint_pathgroup(buff, PRINT_JSON_GROUP, pgp)) < 0 ||
+                   (rc = print_strbuf(buff, PRINT_JSON_GROUP_NUM, i + 1)) < 0 ||
+                   (rc = snprint_json(buff, 3, PRINT_JSON_START_PATHS)) < 0)
+                       return rc;
 
                vector_foreach_slot (pgp->paths, pp, j) {
-                       fwd += snprint_path(buff + fwd, len - fwd, PRINT_JSON_PATH, pp, 0);
-                       if (fwd >= len)
-                               return fwd;
-
-                       fwd += snprint_json_elem_footer(buff + fwd,
-                                       len - fwd, 3, j + 1 == VECTOR_SIZE(pgp->paths));
-                       if (fwd >= len)
-                               return fwd;
+                       if ((rc = snprint_path(buff, PRINT_JSON_PATH,
+                                              pp, 0)) < 0 ||
+                           (rc = snprint_json_elem_footer(
+                                   buff, 3,
+                                   j + 1 == VECTOR_SIZE(pgp->paths))) < 0)
+                               return rc;
                }
-               fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-               if (fwd >= len)
-                       return fwd;
-
-               fwd +=  snprint_json_elem_footer(buff + fwd,
-                               len - fwd, 2, i + 1 == VECTOR_SIZE(mpp->pg));
-               if (fwd >= len)
-                       return fwd;
+               if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+                   (rc = snprint_json_elem_footer(
+                           buff, 2, i + 1 == VECTOR_SIZE(mpp->pg))) < 0)
+                       return rc;
        }
 
-       fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-       if (fwd >= len)
-               return fwd;
+       if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+           (rc = snprint_json_elem_footer(buff, 1, last)) < 0)
+               return rc;
 
-       fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 1, last);
-       return fwd;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-int
-snprint_multipath_map_json (char * buff, int len, const struct multipath * mpp)
+int snprint_multipath_map_json(struct strbuf *buff, const struct multipath * mpp)
 {
-       int fwd = 0;
-
-       fwd +=  snprint_json_header(buff, len);
-       if (fwd >= len)
-               return len;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc;
 
-       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_START_MAP);
-       if (fwd >= len)
-               return len;
+       if ((rc = snprint_json_header(buff)) < 0 ||
+           (rc = snprint_json(buff, 0, PRINT_JSON_START_MAP)) < 0)
+               return rc;
 
-       fwd += snprint_multipath_fields_json(buff + fwd, len - fwd, mpp, 1);
-       if (fwd >= len)
-               return len;
+       if ((rc = snprint_multipath_fields_json(buff, mpp, 1)) < 0)
+               return rc;
 
-       fwd +=  snprint_json(buff + fwd, len - fwd, 0, "\n");
-       if (fwd >= len)
-               return len;
+       if ((rc = snprint_json(buff, 0, "\n")) < 0 ||
+           (rc = snprint_json(buff, 0, PRINT_JSON_END_LAST)) < 0)
+               return rc;
 
-       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
-       if (fwd >= len)
-               return len;
-       return fwd;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-int
-snprint_multipath_topology_json (char * buff, int len, const struct vectors * vecs)
+int snprint_multipath_topology_json (struct strbuf *buff,
+                                    const struct vectors * vecs)
 {
-       int i, fwd = 0;
+       int i;
        struct multipath * mpp;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc;
 
-       fwd +=  snprint_json_header(buff, len);
-       if (fwd >= len)
-               return len;
-
-       fwd +=  snprint_json(buff + fwd, len  - fwd, 1, PRINT_JSON_START_MAPS);
-       if (fwd >= len)
-               return len;
+       if ((rc = snprint_json_header(buff)) < 0 ||
+           (rc = snprint_json(buff, 1, PRINT_JSON_START_MAPS)) < 0)
+               return rc;
 
        vector_foreach_slot(vecs->mpvec, mpp, i) {
-               fwd += snprint_multipath_fields_json(buff + fwd, len - fwd,
-                               mpp, i + 1 == VECTOR_SIZE(vecs->mpvec));
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_multipath_fields_json(
+                            buff, mpp, i + 1 == VECTOR_SIZE(vecs->mpvec))) < 0)
+                       return rc;
        }
 
-       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-       if (fwd >= len)
-               return len;
+       if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+           (rc = snprint_json(buff, 0, PRINT_JSON_END_LAST)) < 0)
+               return rc;
 
-       fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
-       if (fwd >= len)
-               return len;
-       return fwd;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 static int
 snprint_hwentry (const struct config *conf,
-                char * buff, int len, const struct hwentry * hwe)
+                struct strbuf *buff, const struct hwentry * hwe)
 {
-       int i;
-       int fwd = 0;
+       int i, rc;
        struct keyword * kw;
        struct keyword * rootkw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "devices");
-
-       if (!rootkw || !rootkw->sub)
-               return 0;
-
+       assert(rootkw && rootkw->sub);
        rootkw = find_keyword(conf->keywords, rootkw->sub, "device");
+       assert(rootkw);
 
-       if (!rootkw)
-               return 0;
+       if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
+               return rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-       if (fwd >= len)
-               return len;
        iterate_sub_keywords(rootkw, kw, i) {
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                               kw, hwe);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, hwe)) < 0)
+                       return rc;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+       if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_hwtable(const struct config *conf,
-                          char *buff, int len,
+static int snprint_hwtable(const struct config *conf, struct strbuf *buff,
                           const struct _vector *hwtable)
 {
-       int fwd = 0;
-       int i;
+       int i, rc;
        struct hwentry * hwe;
        struct keyword * rootkw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "devices");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
+
+       if ((rc = append_strbuf_str(buff, "devices {\n")) < 0)
+               return rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "devices {\n");
-       if (fwd >= len)
-               return len;
        vector_foreach_slot (hwtable, hwe, i) {
-               fwd += snprint_hwentry(conf, buff + fwd, len - fwd, hwe);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_hwentry(conf, buff, hwe)) < 0)
+                       return rc;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+
+       return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_mpentry (const struct config *conf, char * buff, int len,
+snprint_mpentry (const struct config *conf, struct strbuf *buff,
                 const struct mpentry * mpe, const struct _vector *mpvec)
 {
-       int i;
-       int fwd = 0;
+       int i, rc;
        struct keyword * kw;
        struct keyword * rootkw;
        struct multipath *mpp = NULL;
+       size_t initial_len = get_strbuf_len(buff);
 
        if (mpvec != NULL && (mpp = find_mp_by_wwid(mpvec, mpe->wwid)) == NULL)
                return 0;
 
        rootkw = find_keyword(conf->keywords, NULL, "multipath");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
+
+       if ((rc = append_strbuf_str(buff, "\tmultipath {\n")) < 0)
+               return rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "\tmultipath {\n");
-       if (fwd >= len)
-               return len;
        iterate_sub_keywords(rootkw, kw, i) {
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                               kw, mpe);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, mpe)) < 0)
+                       return rc;
        }
        /*
         * This mpp doesn't have alias defined. Add the alias in a comment.
         */
-       if (mpp != NULL && strcmp(mpp->alias, mpp->wwid)) {
-               fwd += snprintf(buff + fwd, len - fwd, "\t\t# alias \"%s\"\n",
-                               mpp->alias);
-               if (fwd >= len)
-                       return len;
-       }
-       fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+       if (mpp != NULL && strcmp(mpp->alias, mpp->wwid) &&
+           (rc = print_strbuf(buff, "\t\t# alias \"%s\"\n", mpp->alias)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+               return rc;
+
+       return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_mptable(const struct config *conf,
-                          char *buff, int len, const struct _vector *mpvec)
+static int snprint_mptable(const struct config *conf, struct strbuf *buff,
+                          const struct _vector *mpvec)
 {
-       int fwd = 0;
-       int i;
+       int i, rc;
        struct mpentry * mpe;
        struct keyword * rootkw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "multipaths");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
+
+       if ((rc = append_strbuf_str(buff, "multipaths {\n")) < 0)
+               return rc;
 
-       fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n");
-       if (fwd >= len)
-               return len;
        vector_foreach_slot (conf->mptable, mpe, i) {
-               fwd += snprint_mpentry(conf, buff + fwd, len - fwd, mpe, mpvec);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_mpentry(conf, buff, mpe, mpvec)) < 0)
+                       return rc;
        }
        if (mpvec != NULL) {
                struct multipath *mpp;
@@ -1555,496 +1463,373 @@ static int snprint_mptable(const struct config *conf,
                        if (find_mpe(conf->mptable, mpp->wwid) != NULL)
                                continue;
 
-                       fwd += snprintf(buff + fwd, len - fwd,
-                                       "\tmultipath {\n");
-                       if (fwd >= len)
-                               return len;
-                       fwd += snprintf(buff + fwd, len - fwd,
-                                       "\t\twwid \"%s\"\n", mpp->wwid);
-                       if (fwd >= len)
-                               return len;
+                       if ((rc = print_strbuf(buff,
+                                              "\tmultipath {\n\t\twwid \"%s\"\n",
+                                              mpp->wwid)) < 0)
+                               return rc;
                        /*
                         * This mpp doesn't have alias defined in
                         * multipath.conf - otherwise find_mpe would have
                         * found it. Add the alias in a comment.
                         */
-                       if (strcmp(mpp->alias, mpp->wwid)) {
-                               fwd += snprintf(buff + fwd, len - fwd,
-                                               "\t\t# alias \"%s\"\n",
-                                               mpp->alias);
-                               if (fwd >= len)
-                                       return len;
-                       }
-                       fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-                       if (fwd >= len)
-                               return len;
+                       if (strcmp(mpp->alias, mpp->wwid) &&
+                           (rc = print_strbuf(buff, "\t\t# alias \"%s\"\n",
+                                              mpp->alias)) < 0)
+                               return rc;
+                       if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+                               return rc;
                }
        }
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_overrides(const struct config *conf, char * buff, int len,
+static int snprint_overrides(const struct config *conf, struct strbuf *buff,
                             const struct hwentry *overrides)
 {
-       int fwd = 0;
-       int i;
+       int i, rc;
        struct keyword *rootkw;
        struct keyword *kw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "overrides");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
 
-       fwd += snprintf(buff + fwd, len - fwd, "overrides {\n");
-       if (fwd >= len)
-               return len;
+       if ((rc = append_strbuf_str(buff, "overrides {\n")) < 0)
+               return rc;
        if (!overrides)
                goto out;
+
        iterate_sub_keywords(rootkw, kw, i) {
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, NULL);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
+                       return rc;
        }
 out:
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_defaults(const struct config *conf, char *buff, int len)
+static int snprint_defaults(const struct config *conf, struct strbuf *buff)
 {
-       int fwd = 0;
-       int i;
+       int i, rc;
        struct keyword *rootkw;
        struct keyword *kw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "defaults");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
 
-       fwd += snprintf(buff + fwd, len - fwd, "defaults {\n");
-       if (fwd >= len)
-               return len;
+       if ((rc = append_strbuf_str(buff, "defaults {\n")) < 0)
+               return rc;
 
        iterate_sub_keywords(rootkw, kw, i) {
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                               kw, NULL);
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
+                       return rc;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-static int
-snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec)
+static int snprint_blacklist_group(struct strbuf *buff, vector *vec)
 {
-       int threshold = MAX_LINE_LEN;
        struct blentry * ble;
-       int pos;
-       int i;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc, i;
 
-       pos = *fwd;
        if (!VECTOR_SIZE(*vec)) {
-               if ((len - pos - threshold) <= 0)
-                       return 0;
-               pos += snprintf(buff + pos, len - pos, "        <empty>\n");
+               if ((rc = append_strbuf_str(buff, "        <empty>\n")) < 0)
+                       return rc;
        } else vector_foreach_slot (*vec, ble, i) {
-               if ((len - pos - threshold) <= 0)
-                       return 0;
-               if (ble->origin == ORIGIN_CONFIG)
-                       pos += snprintf(buff + pos, len - pos, "        (config file rule) ");
-               else if (ble->origin == ORIGIN_DEFAULT)
-                       pos += snprintf(buff + pos, len - pos, "        (default rule)     ");
-               pos += snprintf(buff + pos, len - pos, "%s\n", ble->str);
+               rc = print_strbuf(buff, "        %s %s\n",
+                                  ble->origin == ORIGIN_CONFIG ?
+                                  "(config file rule)" :
+                                  "(default rule)    ", ble->str);
+               if (rc < 0)
+                       return rc;
        }
 
-       *fwd = pos;
-       return pos;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec)
+snprint_blacklist_devgroup (struct strbuf *buff, vector *vec)
 {
-       int threshold = MAX_LINE_LEN;
        struct blentry_device * bled;
-       int pos;
-       int i;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc, i;
 
-       pos = *fwd;
        if (!VECTOR_SIZE(*vec)) {
-               if ((len - pos - threshold) <= 0)
-                       return 0;
-               pos += snprintf(buff + pos, len - pos, "        <empty>\n");
+               if ((rc = append_strbuf_str(buff, "        <empty>\n")) < 0)
+                       return rc;
        } else vector_foreach_slot (*vec, bled, i) {
-               if ((len - pos - threshold) <= 0)
-                       return 0;
-               if (bled->origin == ORIGIN_CONFIG)
-                       pos += snprintf(buff + pos, len - pos, "        (config file rule) ");
-               else if (bled->origin == ORIGIN_DEFAULT)
-                       pos += snprintf(buff + pos, len - pos, "        (default rule)     ");
-               pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product);
+               rc = print_strbuf(buff, "        %s %s:%s\n",
+                                 bled->origin == ORIGIN_CONFIG ?
+                                 "(config file rule)" :
+                                 "(default rule)    ",
+                                 bled->vendor, bled->product);
+               if (rc < 0)
+                       return rc;
        }
 
-       *fwd = pos;
-       return pos;
-}
-
-int snprint_blacklist_report(struct config *conf, char *buff, int len)
-{
-       int threshold = MAX_LINE_LEN;
-       int fwd = 0;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n"
-                                              "- blacklist:\n");
-       if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode))
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-       if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n"
-                                              "- blacklist:\n");
-       if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property))
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-       if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "protocol rules:\n"
-                                              "- blacklist:\n");
-       if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_protocol))
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-       if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_protocol) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n"
-                                              "- blacklist:\n");
-       if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-       if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_wwid) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "device rules:\n"
-                                              "- blacklist:\n");
-       if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0)
-               return len;
-
-       if ((len - fwd - threshold) <= 0)
-               return len;
-       fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-       if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0)
-               return len;
-
-       if (fwd > len)
-               return len;
-       return fwd;
-}
-
-static int snprint_blacklist(const struct config *conf, char *buff, int len)
+       return get_strbuf_len(buff) - initial_len;
+}
+
+int snprint_blacklist_report(struct config *conf, struct strbuf *buff)
 {
-       int i;
+       size_t initial_len = get_strbuf_len(buff);
+       int rc;
+
+       if ((rc = append_strbuf_str(buff, "device node rules:\n- blacklist:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->blist_devnode)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->elist_devnode)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "udev property rules:\n- blacklist:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->blist_property)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->elist_property)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "protocol rules:\n- blacklist:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->blist_protocol)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->elist_protocol)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "wwid rules:\n- blacklist:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->blist_wwid)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_group(buff, &conf->elist_wwid)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "device rules:\n- blacklist:\n")) < 0)
+               return rc;
+       if ((rc = snprint_blacklist_devgroup(buff, &conf->blist_device)) < 0)
+               return rc;
+
+       if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+            return rc;
+       if ((rc = snprint_blacklist_devgroup(buff, &conf->elist_device)) < 0)
+               return rc;
+
+       return get_strbuf_len(buff) - initial_len;
+}
+
+static int snprint_blacklist(const struct config *conf, struct strbuf *buff)
+{
+       int i, rc;
        struct blentry * ble;
        struct blentry_device * bled;
-       int fwd = 0;
        struct keyword *rootkw;
-       struct keyword *kw;
+       struct keyword *kw, *pkw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "blacklist");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
 
-       fwd += snprintf(buff + fwd, len - fwd, "blacklist {\n");
-       if (fwd >= len)
-               return len;
+       if ((rc = append_strbuf_str(buff, "blacklist {\n")) < 0)
+               return rc;
 
        vector_foreach_slot (conf->blist_devnode, ble, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "devnode");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ble);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->blist_wwid, ble, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "wwid");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ble);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->blist_property, ble, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "property");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ble);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->blist_protocol, ble, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "protocol");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ble);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+                       return rc;
        }
+
        rootkw = find_keyword(conf->keywords, rootkw->sub, "device");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
+       kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
+       pkw = find_keyword(conf->keywords, rootkw->sub, "product");
+       assert(kw && pkw);
 
        vector_foreach_slot (conf->blist_device, bled, i) {
-               fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-               if (fwd >= len)
-                       return len;
-               kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                                      kw, bled);
-               if (fwd >= len)
-                       return len;
-               kw = find_keyword(conf->keywords, rootkw->sub, "product");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                                      kw, bled);
-               if (fwd >= len)
-                       return len;
-               fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\tdevice {\n\t\t%k %v\n",
+                                         kw, bled)) < 0)
+                       return rc;
+               if ((rc = snprint_keyword(buff, "\t\t%k %v\n\t}\n",
+                                         pkw, bled)) < 0)
+                       return rc;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 static int snprint_blacklist_except(const struct config *conf,
-                                   char *buff, int len)
+                                   struct strbuf *buff)
 {
-       int i;
+       int i, rc;
        struct blentry * ele;
        struct blentry_device * eled;
-       int fwd = 0;
        struct keyword *rootkw;
-       struct keyword *kw;
+       struct keyword *kw, *pkw;
+       size_t initial_len = get_strbuf_len(buff);
 
        rootkw = find_keyword(conf->keywords, NULL, "blacklist_exceptions");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
 
-       fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n");
-       if (fwd >= len)
-               return len;
+       if ((rc = append_strbuf_str(buff, "blacklist_exceptions {\n")) < 0)
+               return rc;
 
        vector_foreach_slot (conf->elist_devnode, ele, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "devnode");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ele);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->elist_wwid, ele, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "wwid");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ele);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->elist_property, ele, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "property");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ele);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+                       return rc;
        }
        vector_foreach_slot (conf->elist_protocol, ele, i) {
                kw = find_keyword(conf->keywords, rootkw->sub, "protocol");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-                                      kw, ele);
-               if (fwd >= len)
-                       return len;
+               assert(kw);
+               if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+                       return rc;
        }
+
        rootkw = find_keyword(conf->keywords, rootkw->sub, "device");
-       if (!rootkw)
-               return 0;
+       assert(rootkw);
+       kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
+       pkw = find_keyword(conf->keywords, rootkw->sub, "product");
+       assert(kw && pkw);
 
        vector_foreach_slot (conf->elist_device, eled, i) {
-               fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-               if (fwd >= len)
-                       return len;
-               kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                                      kw, eled);
-               if (fwd >= len)
-                       return len;
-               kw = find_keyword(conf->keywords, rootkw->sub, "product");
-               if (!kw)
-                       return 0;
-               fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-                                      kw, eled);
-               if (fwd >= len)
-                       return len;
-               fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-               if (fwd >= len)
-                       return len;
+               if ((rc = snprint_keyword(buff, "\tdevice {\n\t\t%k %v\n",
+                                         kw, eled)) < 0)
+                       return rc;
+               if ((rc = snprint_keyword(buff, "\t\t%k %v\n\t}\n",
+                                         pkw, eled)) < 0)
+                       return rc;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "}\n");
-       if (fwd >= len)
-               return len;
-       return fwd;
+
+       if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 char *snprint_config(const struct config *conf, int *len,
                     const struct _vector *hwtable, const struct _vector *mpvec)
 {
+       STRBUF_ON_STACK(buff);
        char *reply;
-       /* built-in config is >20kB already */
-       unsigned int maxlen = 32768;
-
-       for (reply = NULL; maxlen <= UINT_MAX/2; maxlen *= 2) {
-               char *c, *tmp = reply;
-
-               reply = REALLOC(reply, maxlen);
-               if (!reply) {
-                       if (tmp)
-                               free(tmp);
+       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,
+                                 hwtable ? hwtable : conf->hwtable)) < 0 ||
+           (rc = snprint_overrides(conf, &buff, conf->overrides)) < 0)
+               return NULL;
+       if (VECTOR_SIZE(conf->mptable) > 0 ||
+           (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
+               if ((rc = snprint_mptable(conf, &buff, mpvec)) < 0)
                        return NULL;
-               }
-
-               c = reply + snprint_defaults(conf, reply, maxlen);
-               if (c == reply + maxlen)
-                       continue;
-
-               c += snprint_blacklist(conf, c, reply + maxlen - c);
-               if (c == reply + maxlen)
-                       continue;
-
-               c += snprint_blacklist_except(conf, c, reply + maxlen - c);
-               if (c == reply + maxlen)
-                       continue;
-
-               c += snprint_hwtable(conf, c, reply + maxlen - c,
-                                    hwtable ? hwtable : conf->hwtable);
-               if (c == reply + maxlen)
-                       continue;
 
-               c += snprint_overrides(conf, c, reply + maxlen - c,
-                                      conf->overrides);
-               if (c == reply + maxlen)
-                       continue;
-
-               if (VECTOR_SIZE(conf->mptable) > 0 ||
-                   (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
-                       c += snprint_mptable(conf, c, reply + maxlen - c,
-                                            mpvec);
+       if (len)
+               *len = get_strbuf_len(&buff);
+       reply = steal_strbuf_str(&buff);
 
-               if (c < reply + maxlen) {
-                       if (len)
-                               *len = c - reply;
-                       return reply;
-               }
-       }
-
-       free(reply);
-       return NULL;
+       return reply;
 }
 
-int snprint_status(char *buff, int len, const struct vectors *vecs)
+int snprint_status(struct strbuf *buff, const struct vectors *vecs)
 {
-       int fwd = 0;
-       int i;
+       int i, rc;
        unsigned int count[PATH_MAX_STATE] = {0};
+       int monitored_count = 0;
        struct path * pp;
+       size_t initial_len = get_strbuf_len(buff);
 
        vector_foreach_slot (vecs->pathvec, pp, i) {
                count[pp->state]++;
        }
-       fwd += snprintf(buff + fwd, len - fwd, "path checker states:\n");
-       for (i=0; i<PATH_MAX_STATE; i++) {
+       if ((rc = append_strbuf_str(buff, "path checker states:\n")) < 0)
+               return rc;
+       for (i = 0; i < PATH_MAX_STATE; i++) {
                if (!count[i])
                        continue;
-               fwd += snprintf(buff + fwd, len - fwd, "%-20s%u\n",
-                               checker_state_name(i), count[i]);
+               if ((rc = print_strbuf(buff, "%-20s%u\n",
+                                      checker_state_name(i), count[i])) < 0)
+                       return rc;
        }
 
-       int monitored_count = 0;
-
        vector_foreach_slot(vecs->pathvec, pp, i)
                if (pp->fd >= 0)
                        monitored_count++;
-       fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n",
-                       monitored_count, is_uevent_busy()? "True" : "False");
+       if ((rc = print_strbuf(buff, "\npaths: %d\nbusy: %s\n",
+                              monitored_count,
+                              is_uevent_busy()? "True" : "False")) < 0)
+               return rc;
 
-       if (fwd >= len)
-               return len;
-       return fwd;
+       return get_strbuf_len(buff) - initial_len;
 }
 
-int snprint_devices(struct config *conf, char *buff, size_t len,
+int snprint_devices(struct config *conf, struct strbuf *buff,
                    const struct vectors *vecs)
 {
-       size_t fwd = 0;
        int r;
        struct udev_enumerate *enm;
        struct udev_list_entry *item, *first;
-
        struct path * pp;
+       size_t initial_len = get_strbuf_len(buff);
 
        enm = udev_enumerate_new(udev);
        if (!enm)
                return 1;
        udev_enumerate_add_match_subsystem(enm, "block");
 
-       fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
+       if ((r = append_strbuf_str(buff, "available block devices:\n")) < 0)
+               goto out;
        r = udev_enumerate_scan_devices(enm);
        if (r < 0)
                goto out;
@@ -2066,10 +1851,6 @@ int snprint_devices(struct config *conf, char *buff, size_t len,
                        continue;
                }
 
-               fwd += snprintf(buff + fwd, len - fwd, "    %s", devname);
-               if (fwd >= len)
-                       break;
-
                pp = find_path_by_dev(vecs->pathvec, devname);
                if (!pp) {
                        const char *hidden;
@@ -2092,41 +1873,27 @@ int snprint_devices(struct config *conf, char *buff, size_t len,
                } else
                        status = " devnode whitelisted, monitored";
 
-               fwd += snprintf(buff + fwd, len - fwd, " %s\n", status);
+               r = print_strbuf(buff, "    %s %s\n", devname, status);
                udev_device_unref(u_dev);
-               if (fwd >= len)
+               if (r < 0)
                        break;
        }
 out:
        udev_enumerate_unref(enm);
+       if (r < 0)
+               return r;
 
-       if (fwd >= len)
-               return len;
-       return fwd;
+       return get_strbuf_len(buff) - initial_len;
 }
 
 /*
  * stdout printing helpers
  */
-void print_path(struct path *pp, char *style)
-{
-       char line[MAX_LINE_LEN];
-
-       memset(&line[0], 0, MAX_LINE_LEN);
-       snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1);
-       printf("%s", line);
-}
-
-void print_all_paths(vector pathvec, int banner)
-{
-       print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG);
-}
-
-void print_all_paths_custo(vector pathvec, int banner, char *fmt)
+static void print_all_paths_custo(vector pathvec, int banner, const char *fmt)
 {
        int i;
        struct path * pp;
-       char line[MAX_LINE_LEN];
+       STRBUF_ON_STACK(line);
 
        if (!VECTOR_SIZE(pathvec)) {
                if (banner)
@@ -2135,12 +1902,18 @@ void print_all_paths_custo(vector pathvec, int banner, char *fmt)
        }
 
        if (banner)
-               fprintf(stdout, "===== paths list =====\n");
+               append_strbuf_str(&line, "===== paths list =====\n");
 
        get_path_layout(pathvec, 1);
-       snprint_path_header(line, MAX_LINE_LEN, fmt);
-       fprintf(stdout, "%s", line);
+       snprint_path_header(&line, fmt);
 
        vector_foreach_slot (pathvec, pp, i)
-               print_path(pp, fmt);
+               snprint_path(&line, fmt, pp, 1);
+
+       printf("%s", get_strbuf_str(&line));
+}
+
+void print_all_paths(vector pathvec, int banner)
+{
+       print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG);
 }
index 0042ceff5e19a94b16fd838fbbcd85cf9d9dd4f0..c6674a5a6a433601d94a8f8c4cc0b88532a74910 100644 (file)
@@ -2,98 +2,32 @@
 #define _PRINT_H
 #include "dm-generic.h"
 
-#define PRINT_PATH_LONG      "%w %i %d %D %p %t %T %s %o"
-#define PRINT_PATH_INDENT    "%i %d %D %t %T %o"
 #define PRINT_PATH_CHECKER   "%i %d %D %p %t %T %o %C"
 #define PRINT_MAP_STATUS     "%n %F %Q %N %t %r"
 #define PRINT_MAP_STATS      "%n %0 %1 %2 %3 %4"
 #define PRINT_MAP_NAMES      "%n %d %w"
-#define PRINT_MAP_PROPS      "size=%S features='%f' hwhandler='%h' wp=%r"
-#define PRINT_PG_INDENT      "policy='%s' prio=%p status=%t"
 
-#define PRINT_JSON_MULTIPLIER     5
-#define PRINT_JSON_MAJOR_VERSION  0
-#define PRINT_JSON_MINOR_VERSION  1
-#define PRINT_JSON_START_VERSION  "   \"major_version\": %d,\n" \
-                                 "   \"minor_version\": %d,\n"
-#define PRINT_JSON_START_ELEM     "{\n"
-#define PRINT_JSON_START_MAP      "   \"map\":"
-#define PRINT_JSON_START_MAPS     "\"maps\": ["
-#define PRINT_JSON_START_PATHS    "\"paths\": ["
-#define PRINT_JSON_START_GROUPS   "\"path_groups\": ["
-#define PRINT_JSON_END_ELEM       "},"
-#define PRINT_JSON_END_LAST_ELEM  "}"
-#define PRINT_JSON_END_LAST       "}\n"
-#define PRINT_JSON_END_ARRAY      "]\n"
-#define PRINT_JSON_INDENT    "   "
-#define PRINT_JSON_MAP       "{\n" \
-                            "      \"name\" : \"%n\",\n" \
-                            "      \"uuid\" : \"%w\",\n" \
-                            "      \"sysfs\" : \"%d\",\n" \
-                            "      \"failback\" : \"%F\",\n" \
-                            "      \"queueing\" : \"%Q\",\n" \
-                            "      \"paths\" : %N,\n" \
-                            "      \"write_prot\" : \"%r\",\n" \
-                            "      \"dm_st\" : \"%t\",\n" \
-                            "      \"features\" : \"%f\",\n" \
-                            "      \"hwhandler\" : \"%h\",\n" \
-                            "      \"action\" : \"%A\",\n" \
-                            "      \"path_faults\" : %0,\n" \
-                            "      \"vend\" : \"%v\",\n" \
-                            "      \"prod\" : \"%p\",\n" \
-                            "      \"rev\" : \"%e\",\n" \
-                            "      \"switch_grp\" : %1,\n" \
-                            "      \"map_loads\" : %2,\n" \
-                            "      \"total_q_time\" : %3,\n" \
-                            "      \"q_timeouts\" : %4,"
-
-#define PRINT_JSON_GROUP     "{\n" \
-                            "         \"selector\" : \"%s\",\n" \
-                            "         \"pri\" : %p,\n" \
-                            "         \"dm_st\" : \"%t\",\n" \
-                            "         \"marginal_st\" : \"%M\","
-
-#define PRINT_JSON_GROUP_NUM "         \"group\" : %d,\n"
-
-#define PRINT_JSON_PATH      "{\n" \
-                            "            \"dev\" : \"%d\",\n"\
-                            "            \"dev_t\" : \"%D\",\n" \
-                            "            \"dm_st\" : \"%t\",\n" \
-                            "            \"dev_st\" : \"%o\",\n" \
-                            "            \"chk_st\" : \"%T\",\n" \
-                            "            \"checker\" : \"%c\",\n" \
-                            "            \"pri\" : %p,\n" \
-                            "            \"host_wwnn\" : \"%N\",\n" \
-                            "            \"target_wwnn\" : \"%n\",\n" \
-                            "            \"host_wwpn\" : \"%R\",\n" \
-                            "            \"target_wwpn\" : \"%r\",\n" \
-                            "            \"host_adapter\" : \"%a\",\n" \
-                            "            \"marginal_st\" : \"%M\""
-
-#define MAX_LINE_LEN  80
-#define MAX_LINES     64
-#define MAX_FIELD_LEN 128
-#define PROGRESS_LEN  10
+struct strbuf;
 
 struct path_data {
        char wildcard;
        char * header;
        unsigned int width;
-       int (*snprint)(char * buff, size_t len, const struct path * pp);
+       int (*snprint)(struct strbuf *, const struct path * pp);
 };
 
 struct multipath_data {
        char wildcard;
        char * header;
        unsigned int width;
-       int (*snprint)(char * buff, size_t len, const struct multipath * mpp);
+       int (*snprint)(struct strbuf *, const struct multipath * mpp);
 };
 
 struct pathgroup_data {
        char wildcard;
        char * header;
        unsigned int width;
-       int (*snprint)(char * buff, size_t len, const struct pathgroup * pgp);
+       int (*snprint)(struct strbuf *, const struct pathgroup * pgp);
 };
 
 enum layout_reset {
@@ -106,37 +40,35 @@ 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 (char *, int, const char *);
-int snprint_multipath_header (char *, int, const char *);
-int _snprint_path (const struct gen_path *, char *, int, const char *, int);
-#define snprint_path(buf, len, fmt, pp, v) \
-       _snprint_path(dm_path_to_gen(pp), buf, len, fmt,  v)
-int _snprint_multipath (const struct gen_multipath *, char *, int,
+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)
+int _snprint_multipath (const struct gen_multipath *, struct strbuf *,
                        const char *, int);
-#define snprint_multipath(buf, len, fmt, mp, v)                                \
-       _snprint_multipath(dm_multipath_to_gen(mp), buf, len, fmt,  v)
-int _snprint_multipath_topology (const struct gen_multipath *, char *, int,
+#define snprint_multipath(buf, fmt, mp, v)                             \
+       _snprint_multipath(dm_multipath_to_gen(mp), buf, fmt,  v)
+int _snprint_multipath_topology (const struct gen_multipath *, struct strbuf *,
                                 int verbosity);
-#define snprint_multipath_topology(buf, len, mpp, v) \
-       _snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, len, v)
-int snprint_multipath_topology_json (char * buff, int len,
-                               const struct vectors * vecs);
+#define snprint_multipath_topology(buf, mpp, v) \
+       _snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v)
+int snprint_multipath_topology_json(struct strbuf *, const struct vectors *vecs);
 char *snprint_config(const struct config *conf, int *len,
                     const struct _vector *hwtable,
                     const struct _vector *mpvec);
-int snprint_multipath_map_json (char * buff, int len,
-                               const struct multipath * mpp);
-int snprint_blacklist_report (struct config *, char *, int);
-int snprint_wildcards (char *, int);
-int snprint_status (char *, int, const struct vectors *);
-int snprint_devices (struct config *, char *, size_t, const struct vectors *);
-int snprint_path_serial (char *, size_t, const struct path *);
-int snprint_host_wwnn (char *, size_t, const struct path *);
-int snprint_host_wwpn (char *, size_t, const struct path *);
-int snprint_tgt_wwnn (char *, size_t, const struct path *);
-int snprint_tgt_wwpn (char *, size_t, const struct path *);
+int snprint_multipath_map_json(struct strbuf *, const struct multipath *mpp);
+int snprint_blacklist_report(struct config *, struct strbuf *);
+int snprint_wildcards(struct strbuf *);
+int snprint_status(struct strbuf *, const struct vectors *);
+int snprint_devices(struct config *, struct strbuf *, const struct vectors *);
+int snprint_path_serial(struct strbuf *, const struct path *);
+int snprint_host_wwnn(struct strbuf *, const struct path *);
+int snprint_host_wwpn(struct strbuf *, const struct path *);
+int snprint_tgt_wwnn(struct strbuf *, const struct path *);
+int snprint_tgt_wwpn(struct strbuf *, const struct path *);
 #define PROTOCOL_BUF_SIZE sizeof("scsi:unspec")
-int snprint_path_protocol(char *, size_t, const struct path *);
+int snprint_path_protocol(struct strbuf *, const struct path *);
 
 void _print_multipath_topology (const struct gen_multipath * gmp,
                                int verbosity);
@@ -144,14 +76,13 @@ void _print_multipath_topology (const struct gen_multipath * gmp,
        _print_multipath_topology(dm_multipath_to_gen(mpp), v)
 
 void print_all_paths (vector pathvec, int banner);
-void print_all_paths_custo (vector pathvec, int banner, char *fmt);
 
 int snprint_path_attr(const struct gen_path* gp,
-                     char *buf, int len, char wildcard);
+                     struct strbuf *buf, char wildcard);
 int snprint_pathgroup_attr(const struct gen_pathgroup* gpg,
-                          char *buf, int len, char wildcard);
+                          struct strbuf *buf, char wildcard);
 int snprint_multipath_attr(const struct gen_multipath* gm,
-                          char *buf, int len, char wildcard);
+                          struct strbuf *buf, char wildcard);
 int snprint_multipath_style(const struct gen_multipath *gmp,
-                           char *style, int len, int verbosity);
+                           struct strbuf *style, int verbosity);
 #endif /* _PRINT_H */
index 916970df839677412ac0817d302b30f361c47535..ea03fc3db60d97d905e3bf408b1a0801423acf91 100644 (file)
 #include "structs_vec.h"
 #include "print.h"
 #include "util.h"
-
-#define CHECK_LEN \
-do { \
-       if ((p - str) >= (len - 1)) { \
-               condlog(0, "%s: %s - buffer size too small", pp->dev, pp->prio.name); \
-               return -1; \
-       } \
-} while(0)
+#include "strbuf.h"
 
 static int
-build_serial_path(struct path *pp, char *str, int len)
+build_serial_path(struct path *pp, struct strbuf *buf)
 {
-       char *p = str;
+       int rc = snprint_path_serial(buf, pp);
 
-       p += snprint_path_serial(p, str + len - p, pp);
-       CHECK_LEN;
-       return 0;
+       return rc < 0 ? rc : 0;
 }
 
 static int
-build_wwn_path(struct path *pp, char *str, int len)
+build_wwn_path(struct path *pp, struct strbuf *buf)
 {
-       char *p = str;
-
-       p += snprint_host_wwnn(p, str + len - p, pp);
-       CHECK_LEN;
-       p += snprintf(p, str + len - p, ":");
-       CHECK_LEN;
-       p += snprint_host_wwpn(p, str + len - p, pp);
-       CHECK_LEN;
-       p += snprintf(p, str + len - p, ":");
-       CHECK_LEN;
-       p += snprint_tgt_wwnn(p, str + len - p, pp);
-       CHECK_LEN;
-       p += snprintf(p, str + len - p, ":");
-       CHECK_LEN;
-       p += snprint_tgt_wwpn(p, str + len - p, pp);
-       CHECK_LEN;
+       int rc;
+
+       if ((rc = snprint_host_wwnn(buf, pp)) < 0 ||
+           (rc = fill_strbuf(buf, ':', 1)) < 0 ||
+           (rc = snprint_host_wwpn(buf, pp)) < 0 ||
+           (rc = fill_strbuf(buf, ':', 1)) < 0 ||
+           (rc = snprint_tgt_wwnn(buf, pp) < 0) ||
+           (rc = fill_strbuf(buf, ':', 1) < 0) ||
+           (rc = snprint_tgt_wwpn(buf, pp) < 0))
+               return rc;
        return 0;
 }
 
 /* main priority routine */
 int prio_path_weight(struct path *pp, char *prio_args)
 {
-       char path[FILE_NAME_SIZE];
-       char *arg;
+       STRBUF_ON_STACK(path);
+       char *arg __attribute__((cleanup(cleanup_charp))) = NULL;
        char *temp, *regex, *prio;
        char split_char[] = " \t";
        int priority = DEFAULT_PRIORITY, path_found = 0;
@@ -101,24 +86,22 @@ int prio_path_weight(struct path *pp, char *prio_args)
        }
 
        if (!strcmp(regex, HBTL)) {
-               sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no,
-                       pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun);
+               if (print_strbuf(&path, "%d:%d:%d:%" PRIu64, pp->sg_id.host_no,
+                                pp->sg_id.channel, pp->sg_id.scsi_id,
+                                pp->sg_id.lun) < 0)
+                       return priority;
        } else if (!strcmp(regex, DEV_NAME)) {
-               strcpy(path, pp->dev);
+               if (append_strbuf_str(&path, pp->dev) < 0)
+                       return priority;
        } else if (!strcmp(regex, SERIAL)) {
-               if (build_serial_path(pp, path, FILE_NAME_SIZE) != 0) {
-                       FREE(arg);
+               if (build_serial_path(pp, &path) != 0)
                        return priority;
-               }
        } else if (!strcmp(regex, WWN)) {
-               if (build_wwn_path(pp, path, FILE_NAME_SIZE) != 0) {
-                       FREE(arg);
+               if (build_wwn_path(pp, &path) != 0)
                        return priority;
-               }
        } else {
                condlog(0, "%s: %s - Invalid arguments", pp->dev,
                        pp->prio.name);
-               FREE(arg);
                return priority;
        }
 
@@ -131,7 +114,8 @@ int prio_path_weight(struct path *pp, char *prio_args)
                        break;
 
                if (!regcomp(&pathe, regex, REG_EXTENDED|REG_NOSUB)) {
-                       if (!regexec(&pathe, path, 0, NULL, 0)) {
+                       if (!regexec(&pathe, get_strbuf_str(&path), 0,
+                                    NULL, 0)) {
                                path_found = 1;
                                priority = atoi(prio);
                        }
@@ -139,7 +123,6 @@ int prio_path_weight(struct path *pp, char *prio_args)
                }
        }
 
-       FREE(arg);
        return priority;
 }
 
index b7b3379193021d6357f0025fcd4977b23ffcae7f..b287667049398075356f796e66dc12915922447a 100644 (file)
@@ -24,6 +24,7 @@
 #include "prioritizers/alua_rtpg.h"
 #include "prkey.h"
 #include "propsel.h"
+#include "strbuf.h"
 #include <inttypes.h>
 #include <libudev.h>
 
@@ -191,7 +192,7 @@ out:
 int select_rr_weight(struct config *conf, struct multipath * mp)
 {
        const char *origin;
-       char buff[13];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(rr_weight);
        mp_set_ovr(rr_weight);
@@ -199,15 +200,16 @@ int select_rr_weight(struct config *conf, struct multipath * mp)
        mp_set_conf(rr_weight);
        mp_set_default(rr_weight, DEFAULT_RR_WEIGHT);
 out:
-       print_rr_weight(buff, 13, mp->rr_weight);
-       condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin);
+       print_rr_weight(&buff, mp->rr_weight);
+       condlog(3, "%s: rr_weight = %s %s", mp->alias,
+               get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_pgfailback(struct config *conf, struct multipath * mp)
 {
        const char *origin;
-       char buff[13];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(pgfailback);
        mp_set_ovr(pgfailback);
@@ -215,8 +217,9 @@ int select_pgfailback(struct config *conf, struct multipath * mp)
        mp_set_conf(pgfailback);
        mp_set_default(pgfailback, DEFAULT_FAILBACK);
 out:
-       print_pgfailback(buff, 13, mp->pgfailback);
-       condlog(3, "%s: failback = %s %s", mp->alias, buff, origin);
+       print_pgfailback(&buff, mp->pgfailback);
+       condlog(3, "%s: failback = %s %s", mp->alias,
+               get_strbuf_str(&buff), origin);
        return 0;
 }
 
@@ -339,7 +342,7 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa
 {
        static const char q_i_n_p[] = "queue_if_no_path";
        static const char r_a_h_h[] = "retain_attached_hw_handler";
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        if (*features == NULL)
                return;
@@ -360,17 +363,15 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa
                        id, q_i_n_p);
                if (*no_path_retry == NO_PATH_RETRY_UNDEF) {
                        *no_path_retry = NO_PATH_RETRY_QUEUE;
-                       print_no_path_retry(buff, sizeof(buff),
-                                           *no_path_retry);
+                       print_no_path_retry(&buff, *no_path_retry);
                        condlog(3, "%s: no_path_retry = %s (inherited setting from feature '%s')",
-                               id, buff, q_i_n_p);
+                               id, get_strbuf_str(&buff), q_i_n_p);
                };
                /* Warn only if features string is overridden */
                if (*no_path_retry != NO_PATH_RETRY_QUEUE) {
-                       print_no_path_retry(buff, sizeof(buff),
-                                           *no_path_retry);
+                       print_no_path_retry(&buff, *no_path_retry);
                        condlog(2, "%s: ignoring feature '%s' because no_path_retry is set to '%s'",
-                               id, q_i_n_p, buff);
+                               id, q_i_n_p, get_strbuf_str(&buff));
                }
                remove_feature(features, q_i_n_p);
        }
@@ -704,7 +705,7 @@ out:
 int select_no_path_retry(struct config *conf, struct multipath *mp)
 {
        const char *origin = NULL;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        if (mp->disable_queueing) {
                condlog(0, "%s: queueing disabled", mp->alias);
@@ -716,10 +717,10 @@ int select_no_path_retry(struct config *conf, struct multipath *mp)
        mp_set_hwe(no_path_retry);
        mp_set_conf(no_path_retry);
 out:
-       print_no_path_retry(buff, 12, mp->no_path_retry);
+       print_no_path_retry(&buff, mp->no_path_retry);
        if (origin)
-               condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff,
-                       origin);
+               condlog(3, "%s: no_path_retry = %s %s", mp->alias,
+                       get_strbuf_str(&buff), origin);
        else
                condlog(3, "%s: no_path_retry = undef %s",
                        mp->alias, default_origin);
@@ -770,22 +771,23 @@ int select_minio(struct config *conf, struct multipath *mp)
 int select_fast_io_fail(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_ovr(fast_io_fail);
        mp_set_hwe(fast_io_fail);
        mp_set_conf(fast_io_fail);
        mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
 out:
-       print_undef_off_zero(buff, 12, mp->fast_io_fail);
-       condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin);
+       print_undef_off_zero(&buff, mp->fast_io_fail);
+       condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias,
+               get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_dev_loss(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_ovr(dev_loss);
        mp_set_hwe(dev_loss);
@@ -793,15 +795,16 @@ int select_dev_loss(struct config *conf, struct multipath *mp)
        mp->dev_loss = DEV_LOSS_TMO_UNSET;
        return 0;
 out:
-       print_dev_loss(buff, 12, mp->dev_loss);
-       condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin);
+       print_dev_loss(&buff, mp->dev_loss);
+       condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias,
+               get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_eh_deadline(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_ovr(eh_deadline);
        mp_set_hwe(eh_deadline);
@@ -810,8 +813,9 @@ int select_eh_deadline(struct config *conf, struct multipath *mp)
        /* not changing sysfs in default cause, so don't print anything */
        return 0;
 out:
-       print_undef_off_zero(buff, 12, mp->eh_deadline);
-       condlog(3, "%s: eh_deadline = %s %s", mp->alias, buff, origin);
+       print_undef_off_zero(&buff, mp->eh_deadline);
+       condlog(3, "%s: eh_deadline = %s %s", mp->alias,
+               get_strbuf_str(&buff), origin);
        return 0;
 }
 
@@ -833,7 +837,7 @@ out:
 int select_reservation_key(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[PRKEY_SIZE];
+       STRBUF_ON_STACK(buff);
        char *from_file = "";
        uint64_t prkey = 0;
 
@@ -851,10 +855,10 @@ out:
                else
                        put_be64(mp->reservation_key, prkey);
        }
-       print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key,
+       print_reservation_key(&buff, mp->reservation_key,
                              mp->sa_flags, mp->prkey_source);
-       condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin,
-               from_file);
+       condlog(3, "%s: reservation_key = %s %s%s", mp->alias,
+               get_strbuf_str(&buff), origin, from_file);
        return 0;
 }
 
@@ -951,16 +955,16 @@ use_delay_watch_checks(struct config *conf, struct multipath *mp)
 {
        int value = NU_UNDEF;
        const char *origin = default_origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        do_set(delay_watch_checks, mp->mpe, value, multipaths_origin);
        do_set(delay_watch_checks, conf->overrides, value, overrides_origin);
        do_set_from_hwe(delay_watch_checks, mp, value, hwe_origin);
        do_set(delay_watch_checks, conf, value, conf_origin);
 out:
-       if (print_off_int_undef(buff, 12, value) != 0)
-               condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff,
-                       origin);
+       if (print_off_int_undef(&buff, value) > 0)
+               condlog(3, "%s: delay_watch_checks = %s %s", mp->alias,
+                       get_strbuf_str(&buff), origin);
        return value;
 }
 
@@ -969,23 +973,23 @@ use_delay_wait_checks(struct config *conf, struct multipath *mp)
 {
        int value = NU_UNDEF;
        const char *origin = default_origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        do_set(delay_wait_checks, mp->mpe, value, multipaths_origin);
        do_set(delay_wait_checks, conf->overrides, value, overrides_origin);
        do_set_from_hwe(delay_wait_checks, mp, value, hwe_origin);
        do_set(delay_wait_checks, conf, value, conf_origin);
 out:
-       if (print_off_int_undef(buff, 12, value) != 0)
-               condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff,
-                       origin);
+       if (print_off_int_undef(&buff, value) > 0)
+               condlog(3, "%s: delay_wait_checks = %s %s", mp->alias,
+                       get_strbuf_str(&buff), origin);
        return value;
 }
 
 int select_delay_checks(struct config *conf, struct multipath *mp)
 {
        int watch_checks, wait_checks;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        watch_checks = use_delay_watch_checks(conf, mp);
        wait_checks = use_delay_wait_checks(conf, mp);
@@ -1001,16 +1005,17 @@ int select_delay_checks(struct config *conf, struct multipath *mp)
                (watch_checks > 0)? delay_watch_origin : delay_wait_origin);
        if (watch_checks > 0) {
                mp->san_path_err_forget_rate = watch_checks;
-               print_off_int_undef(buff, 12, mp->san_path_err_forget_rate);
+               print_off_int_undef(&buff, mp->san_path_err_forget_rate);
                condlog(3, "%s: san_path_err_forget_rate = %s %s", mp->alias,
-                       buff, delay_watch_origin);
+                       get_strbuf_str(&buff), delay_watch_origin);
+               reset_strbuf(&buff);
        }
        if (wait_checks > 0) {
                mp->san_path_err_recovery_time = wait_checks *
                                                 conf->max_checkint;
-               print_off_int_undef(buff, 12, mp->san_path_err_recovery_time);
+               print_off_int_undef(&buff, mp->san_path_err_recovery_time);
                condlog(3, "%s: san_path_err_recovery_time = %s %s", mp->alias,
-                       buff, delay_wait_origin);
+                       get_strbuf_str(&buff), delay_wait_origin);
        }
        return 0;
 }
@@ -1029,7 +1034,7 @@ static int san_path_deprecated_warned;
 int select_san_path_err_threshold(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        if (marginal_path_check_enabled(mp)) {
                mp->san_path_err_threshold = NU_NO;
@@ -1042,9 +1047,9 @@ int select_san_path_err_threshold(struct config *conf, struct multipath *mp)
        mp_set_conf(san_path_err_threshold);
        mp_set_default(san_path_err_threshold, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12, mp->san_path_err_threshold) != 0)
+       if (print_off_int_undef(&buff, mp->san_path_err_threshold) > 0)
                condlog(3, "%s: san_path_err_threshold = %s %s",
-                       mp->alias, buff, origin);
+                       mp->alias, get_strbuf_str(&buff), origin);
        warn_san_path_deprecated(mp, san_path_err_threshold);
        return 0;
 }
@@ -1052,7 +1057,7 @@ out:
 int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        if (marginal_path_check_enabled(mp)) {
                mp->san_path_err_forget_rate = NU_NO;
@@ -1065,9 +1070,9 @@ int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp)
        mp_set_conf(san_path_err_forget_rate);
        mp_set_default(san_path_err_forget_rate, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12, mp->san_path_err_forget_rate) != 0)
-               condlog(3, "%s: san_path_err_forget_rate = %s %s", mp->alias,
-                       buff, origin);
+       if (print_off_int_undef(&buff, mp->san_path_err_forget_rate) > 0)
+               condlog(3, "%s: san_path_err_forget_rate = %s %s",
+                       mp->alias, get_strbuf_str(&buff), origin);
        warn_san_path_deprecated(mp, san_path_err_forget_rate);
        return 0;
 
@@ -1076,7 +1081,7 @@ out:
 int select_san_path_err_recovery_time(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        if (marginal_path_check_enabled(mp)) {
                mp->san_path_err_recovery_time = NU_NO;
@@ -1089,9 +1094,9 @@ int select_san_path_err_recovery_time(struct config *conf, struct multipath *mp)
        mp_set_conf(san_path_err_recovery_time);
        mp_set_default(san_path_err_recovery_time, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12, mp->san_path_err_recovery_time) != 0)
+       if (print_off_int_undef(&buff, mp->san_path_err_recovery_time) != 0)
                condlog(3, "%s: san_path_err_recovery_time = %s %s", mp->alias,
-                       buff, origin);
+                       get_strbuf_str(&buff), origin);
        warn_san_path_deprecated(mp, san_path_err_recovery_time);
        return 0;
 
@@ -1100,7 +1105,7 @@ out:
 int select_marginal_path_err_sample_time(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(marginal_path_err_sample_time);
        mp_set_ovr(marginal_path_err_sample_time);
@@ -1114,17 +1119,16 @@ out:
                        mp->alias, 2 * IOTIMEOUT_SEC);
                        mp->marginal_path_err_sample_time = 2 * IOTIMEOUT_SEC;
        }
-       if (print_off_int_undef(buff, 12, mp->marginal_path_err_sample_time)
-           != 0)
+       if (print_off_int_undef(&buff, mp->marginal_path_err_sample_time) > 0)
                condlog(3, "%s: marginal_path_err_sample_time = %s %s",
-                       mp->alias, buff, origin);
+                       mp->alias, get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(marginal_path_err_rate_threshold);
        mp_set_ovr(marginal_path_err_rate_threshold);
@@ -1132,17 +1136,16 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat
        mp_set_conf(marginal_path_err_rate_threshold);
        mp_set_default(marginal_path_err_rate_threshold, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12, mp->marginal_path_err_rate_threshold)
-           != 0)
+       if (print_off_int_undef(&buff, mp->marginal_path_err_rate_threshold) > 0)
                condlog(3, "%s: marginal_path_err_rate_threshold = %s %s",
-                       mp->alias, buff, origin);
+                       mp->alias, get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(marginal_path_err_recheck_gap_time);
        mp_set_ovr(marginal_path_err_recheck_gap_time);
@@ -1150,17 +1153,17 @@ int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multip
        mp_set_conf(marginal_path_err_recheck_gap_time);
        mp_set_default(marginal_path_err_recheck_gap_time, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12,
-                               mp->marginal_path_err_recheck_gap_time) != 0)
+       if (print_off_int_undef(&buff,
+                               mp->marginal_path_err_recheck_gap_time) > 0)
                condlog(3, "%s: marginal_path_err_recheck_gap_time = %s %s",
-                       mp->alias, buff, origin);
+                       mp->alias, get_strbuf_str(&buff), origin);
        return 0;
 }
 
 int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(marginal_path_double_failed_time);
        mp_set_ovr(marginal_path_double_failed_time);
@@ -1168,10 +1171,9 @@ int select_marginal_path_double_failed_time(struct config *conf, struct multipat
        mp_set_conf(marginal_path_double_failed_time);
        mp_set_default(marginal_path_double_failed_time, DEFAULT_ERR_CHECKS);
 out:
-       if (print_off_int_undef(buff, 12, mp->marginal_path_double_failed_time)
-           != 0)
+       if (print_off_int_undef(&buff, mp->marginal_path_double_failed_time) > 0)
                condlog(3, "%s: marginal_path_double_failed_time = %s %s",
-                       mp->alias, buff, origin);
+                       mp->alias, get_strbuf_str(&buff), origin);
        return 0;
 }
 
@@ -1215,7 +1217,7 @@ out:
 int select_ghost_delay (struct config *conf, struct multipath * mp)
 {
        const char *origin;
-       char buff[12];
+       STRBUF_ON_STACK(buff);
 
        mp_set_mpe(ghost_delay);
        mp_set_ovr(ghost_delay);
@@ -1223,8 +1225,9 @@ int select_ghost_delay (struct config *conf, struct multipath * mp)
        mp_set_conf(ghost_delay);
        mp_set_default(ghost_delay, DEFAULT_GHOST_DELAY);
 out:
-       if (print_off_int_undef(buff, 12, mp->ghost_delay) != 0)
-               condlog(3, "%s: ghost_delay = %s %s", mp->alias, buff, origin);
+       if (print_off_int_undef(&buff, mp->ghost_delay) != 0)
+               condlog(3, "%s: ghost_delay = %s %s", mp->alias,
+                       get_strbuf_str(&buff), origin);
        return 0;
 }
 
diff --git a/libmultipath/strbuf.c b/libmultipath/strbuf.c
new file mode 100644 (file)
index 0000000..a24a57d
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <assert.h>
+#include "strbuf.h"
+
+static const char empty_str[] = "";
+
+const char *get_strbuf_str(const struct strbuf *buf)
+{
+       return buf->buf ? buf->buf : empty_str;
+}
+
+char *steal_strbuf_str(struct strbuf *buf)
+{
+       char *p = buf->buf;
+
+       buf->buf = NULL;
+       buf->size = buf->offs = 0;
+       return p;
+}
+
+size_t get_strbuf_len(const struct strbuf *buf)
+{
+       return buf->offs;
+}
+
+static bool strbuf_is_sane(const struct strbuf *buf)
+{
+       return buf && ((!buf->buf && !buf->size && !buf->offs) ||
+                      (buf->buf && buf->size && buf->size > buf->offs));
+}
+
+void reset_strbuf(struct strbuf *buf)
+{
+       free(buf->buf);
+       buf->buf = NULL;
+       buf->size = buf->offs = 0;
+}
+
+void free_strbuf(struct strbuf *buf)
+{
+       if (!buf)
+               return;
+       reset_strbuf(buf);
+       free(buf);
+}
+
+struct strbuf *new_strbuf(void)
+{
+       return calloc(1, sizeof(struct strbuf));
+}
+
+int truncate_strbuf(struct strbuf *buf, size_t offs)
+{
+       if (!buf->buf)
+               return -EFAULT;
+       if (offs > buf->offs)
+               return -ERANGE;
+
+       buf->offs = offs;
+       buf->buf[offs] = '\0';
+       return 0;
+}
+
+#define BUF_CHUNK 64
+
+static int expand_strbuf(struct strbuf *buf, int addsz)
+{
+       size_t add;
+       char *tmp;
+
+       assert(strbuf_is_sane(buf));
+       if (addsz < 0)
+               return -EINVAL;
+       if (buf->size - buf->offs >= (size_t)addsz + 1)
+               return 0;
+
+       add = ((addsz - (buf->size - buf->offs)) / BUF_CHUNK + 1)
+               * BUF_CHUNK;
+
+       if (buf->size >= SIZE_MAX - add) {
+               add = SIZE_MAX - buf->size;
+               if (add < (size_t)addsz + 1)
+                       return -EOVERFLOW;
+       }
+
+       tmp = realloc(buf->buf, buf->size + add);
+       if (!tmp)
+               return -ENOMEM;
+
+       buf->buf = tmp;
+       buf->size += add;
+       buf->buf[buf->offs] = '\0';
+
+       return 0;
+}
+
+int __append_strbuf_str(struct strbuf *buf, const char *str, int slen)
+{
+       int ret;
+
+       if ((ret = expand_strbuf(buf, slen)) < 0)
+               return ret;
+
+       memcpy(buf->buf + buf->offs, str, slen);
+       buf->offs += slen;
+       buf->buf[buf->offs] = '\0';
+
+       return slen;
+}
+
+int append_strbuf_str(struct strbuf *buf, const char *str)
+{
+       size_t slen;
+
+       if (!str)
+               return -EINVAL;
+
+       slen = strlen(str);
+       if (slen > INT_MAX)
+               return -ERANGE;
+
+       return __append_strbuf_str(buf, str, slen);
+}
+
+int fill_strbuf(struct strbuf *buf, char c, int slen)
+{
+       int ret;
+
+       if ((ret = expand_strbuf(buf, slen)) < 0)
+               return ret;
+
+       memset(buf->buf + buf->offs, c, slen);
+       buf->offs += slen;
+       buf->buf[buf->offs] = '\0';
+
+       return slen;
+}
+
+int append_strbuf_quoted(struct strbuf *buff, const char *ptr)
+{
+       char *quoted, *q;
+       const char *p;
+       unsigned n_quotes, i;
+       size_t qlen;
+       int ret;
+
+       if (!ptr)
+               return -EINVAL;
+
+       for (n_quotes = 0, p = strchr(ptr, '"'); p; p = strchr(++p, '"'))
+               n_quotes++;
+
+       /* leading + trailing quote, 1 extra quote for every quote in ptr */
+       qlen = strlen(ptr) + 2 + n_quotes;
+       if (qlen > INT_MAX)
+               return -ERANGE;
+       if ((ret = expand_strbuf(buff, qlen)) < 0)
+               return ret;
+
+       quoted = &(buff->buf[buff->offs]);
+       *quoted++ = '"';
+       for (p = ptr, q = quoted, i = 0; i < n_quotes; i++) {
+               char *q1 = memccpy(q, p, '"', qlen - 2 - (q - quoted));
+
+               assert(q1 != NULL);
+               p += q1 - q;
+               *q1++ = '"';
+               q = q1;
+       }
+       q = mempcpy(q, p, qlen - 2 - (q - quoted));
+       *q++ = '"';
+       *q = '\0';
+       ret = q - &(buff->buf[buff->offs]);
+       buff->offs += ret;
+       return ret;
+}
+
+__attribute__((format(printf, 2, 3)))
+int print_strbuf(struct strbuf *buf, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+       char *tail;
+
+       va_start(ap, fmt);
+       ret = vasprintf(&tail, fmt, ap);
+       va_end(ap);
+
+       if (ret < 0)
+               return -ENOMEM;
+
+       ret = __append_strbuf_str(buf, tail, ret);
+
+       free(tail);
+       return ret;
+}
diff --git a/libmultipath/strbuf.h b/libmultipath/strbuf.h
new file mode 100644 (file)
index 0000000..5903572
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+#ifndef _STRBUF_H
+#define _STRBUF_H
+#include <errno.h>
+#include <string.h>
+
+struct strbuf {
+       char *buf;
+       size_t size;
+       size_t offs;
+};
+
+/**
+ * reset_strbuf(): prepare strbuf for new content
+ * @param strbuf: string buffer to reset
+ *
+ * Frees internal buffer and resets size and offset to 0.
+ * Can be used to cleanup a struct strbuf on stack.
+ */
+void reset_strbuf(struct strbuf *buf);
+
+/**
+ * free_strbuf(): free resources
+ * @param strbuf: string buffer to discard
+ *
+ * Frees all memory occupied by a struct strbuf.
+ */
+void free_strbuf(struct strbuf *buf);
+
+/**
+ * macro: STRBUF_INIT
+ *
+ * Use this to initialize a local struct strbuf on the stack,
+ * or in a global/static variable.
+ */
+#define STRBUF_INIT { .buf = NULL, }
+
+/**
+ * macro: STRBUF_ON_STACK
+ *
+ * Define and initialize a local struct @strbuf to be cleaned up when
+ * the current scope is left
+ */
+#define STRBUF_ON_STACK(__x)                                           \
+       struct strbuf __attribute__((cleanup(reset_strbuf))) (__x) = STRBUF_INIT;
+
+/**
+ * new_strbuf(): allocate a struct strbuf on the heap
+ *
+ * @returns: pointer to allocated struct, or NULL in case of error.
+ */
+struct strbuf *new_strbuf(void);
+
+/**
+ * get_strbuf_str(): retrieve string from strbuf
+ * @param buf: a struct strbuf
+ * @returns: pointer to the string written to the strbuf so far.
+ *
+ * If @strbuf was never written to, the function returns a zero-
+ * length string. The return value of this function must not be
+ * free()d.
+ */
+const char *get_strbuf_str(const struct strbuf *buf);
+
+/**
+ * steal_strbuf_str(): retrieve string from strbuf and reset
+ * @param buf: a struct strbuf
+ * @returns: pointer to the string written to @strbuf, or NULL
+ *
+ * After calling this function, the @strbuf is empty as if freshly
+ * initialized. The caller is responsible to free() the returned pointer.
+ * If @strbuf was never written to (not even an empty string was appended),
+ * the function returns NULL.
+ */
+char *steal_strbuf_str(struct strbuf *buf);
+
+/**
+ * get_strbuf_len(): retrieve string length from strbuf
+ * @param buf: a struct strbuf
+ * @returns: the length of the string written to @strbuf so far.
+ */
+size_t get_strbuf_len(const struct strbuf *buf);
+
+/**
+ * truncate_strbuf(): shorten the buffer
+ * @param buf: struct strbuf to truncate
+ * @param offs: new buffer position / offset
+ * @returns: 0 on success, negative error code otherwise.
+ *
+ * If @strbuf is freshly allocated/reset (never written to), -EFAULT
+ * is returned. if @offs must be higher than the current offset as returned
+ * by get_strbuf_len(), -ERANGE is returned. The allocated size of the @strbuf
+ * remains unchanged.
+ */
+int truncate_strbuf(struct strbuf *buf, size_t offs);
+
+/**
+ * __append_strbuf_str(): append string of known length
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, not necessarily 0-terminated
+ * @param slen: max number of characters to append, must be non-negative
+ * @returns: @slen = number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise.
+ *
+ * Notes: a 0-byte is always appended to the output buffer after @slen characters.
+ * 0-bytes possibly contained in the first @slen characters are copied into
+ * the output. If the function returns an error, @strbuf is unchanged.
+ */
+int __append_strbuf_str(struct strbuf *buf, const char *str, int slen);
+
+/**
+ * append_strbuf_str(): append string
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, 0-terminated
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given 0-terminated string to @strbuf, expanding @strbuf's size
+ * as necessary. If the function returns an error, @strbuf is unchanged.
+ */
+int append_strbuf_str(struct strbuf *buf, const char *str);
+
+/**
+ * fill_strbuf_str(): pad strbuf with a character
+ * @param buf: the struct strbuf to write to
+ * @param c: the character used for filling
+ * @param slen: max number of characters to append, must be non-negative
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given character @slen times to @strbuf, expanding @strbuf's size
+ * as necessary. If the function returns an error, @strbuf is unchanged.
+ */
+int fill_strbuf(struct strbuf *buf, char c, int slen);
+
+/**
+ * append_strbuf_quoted(): append string in double quotes, escaping quotes in string
+ * @param buf: the struct strbuf to write to
+ * @param str: the string to append, 0-terminated
+ * @returns: number of appended characters if successful (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the given string to @strbuf, with leading and trailing double
+ * quotes (") added, expanding @strbuf's size as necessary. Any double quote
+ * characters (") in the string are transformed to double double quotes ("").
+ * If the function returns an error, @strbuf is unchanged.
+ */
+int append_strbuf_quoted(struct strbuf *buf, const char *str);
+
+/**
+ * print_strbuf(): print to strbuf, formatted
+ * @param buf: the struct strbuf to print to
+ * @param fmt: printf()-like format string
+ * @returns: number of appended characters if successful, (excluding
+ * terminating '\0'); negative error code otherwise
+ *
+ * Appends the the arguments following @fmt, formatted as in printf(), to
+ * @strbuf, expanding @strbuf's size as necessary. The function makes sure that
+ * the output @strbuf is always 0-terminated.
+ * If the function returns an error, @strbuf is unchanged.
+ */
+__attribute__((format(printf, 2, 3)))
+int print_strbuf(struct strbuf *buf, const char *fmt, ...);
+
+#endif
index 8751fc2b21ffb1f5c257772389e33d31de43968c..6e5a1038c329727e76e21a9361ec21e3dd6afc87 100644 (file)
@@ -96,7 +96,7 @@ alloc_path (void)
                pp->sg_id.host_no = -1;
                pp->sg_id.channel = -1;
                pp->sg_id.scsi_id = -1;
-               pp->sg_id.lun = -1;
+               pp->sg_id.lun = SCSI_INVALID_LUN;
                pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
                pp->fd = -1;
                pp->tpgs = TPGS_UNDEF;
index c8447e56d5d6dadf0a70e947825512a0012af67e..399540e784ce1f7f72754329fbc74ac4552f6145 100644 (file)
@@ -13,7 +13,6 @@
 #define SERIAL_SIZE            128
 #define NODE_NAME_SIZE         224
 #define PATH_STR_SIZE          16
-#define PARAMS_SIZE            4096
 #define FILE_NAME_SIZE         256
 #define CALLOUT_MAX_SIZE       256
 #define BLK_DEV_SIZE           33
@@ -178,6 +177,8 @@ enum scsi_protocol {
        SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
 };
 
+#define SCSI_INVALID_LUN ~0ULL
+
 enum no_undef_states {
        NU_NO = -1,
        NU_UNDEF = 0,
@@ -258,7 +259,7 @@ struct sg_id {
        int host_no;
        int channel;
        int scsi_id;
-       int lun;
+       uint64_t lun;
        short h_cmd_per_lun;
        short d_queue_depth;
        enum scsi_protocol proto_id;
index d242c06bcf83faaac4d054211ce4d860886e40c9..85d97ac1cbb035219f9c8bae532711f34efe4fdd 100644 (file)
@@ -45,8 +45,8 @@ int update_mpp_paths(struct multipath *mpp, vector pathvec)
 
                                /*
                                 * Avoid adding removed paths to the map again
-                                * when we reload it. Such paths may exist if
-                                * domap fails in ev_remove_path().
+                                * when we reload it. Such paths may exist in
+                                * ev_remove_paths() or if it returns failure.
                                 */
                                pp1 = find_path_by_devt(pathvec, pp->dev_t);
                                if (pp1 && pp->initialized != INIT_REMOVED &&
@@ -334,7 +334,7 @@ void set_path_removed(struct path *pp)
 }
 
 void
-remove_map(struct multipath *mpp, vector pathvec, vector mpvec, int purge_vec)
+remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
 {
        int i;
 
@@ -343,7 +343,7 @@ remove_map(struct multipath *mpp, vector pathvec, vector mpvec, int purge_vec)
         */
        orphan_paths(pathvec, mpp, "map removed internally");
 
-       if (purge_vec &&
+       if (mpvec &&
            (i = find_slot(mpvec, (void *)mpp)) != -1)
                vector_del_slot(mpvec, i);
 
@@ -354,12 +354,12 @@ remove_map(struct multipath *mpp, vector pathvec, vector mpvec, int purge_vec)
 }
 
 void
-remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec)
+remove_map_by_alias(const char *alias, struct vectors * vecs)
 {
        struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
        if (mpp) {
                condlog(2, "%s: removing map by alias", alias);
-               remove_map(mpp, vecs->pathvec, vecs->mpvec, purge_vec);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec);
        }
 }
 
@@ -373,7 +373,7 @@ remove_maps(struct vectors * vecs)
                return;
 
        vector_foreach_slot (vecs->mpvec, mpp, i) {
-               remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec);
                i--;
        }
 
@@ -416,12 +416,12 @@ int
 update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
 {
        int r = DMP_ERR;
-       char params[PARAMS_SIZE] = {0};
+       char *params = NULL;
 
        if (!mpp)
                return r;
 
-       r = dm_get_map(mpp->alias, &mpp->size, params);
+       r = dm_get_map(mpp->alias, &mpp->size, &params);
        if (r != DMP_OK) {
                condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
                return r;
@@ -429,14 +429,17 @@ update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
 
        if (disassemble_map(pathvec, params, mpp)) {
                condlog(2, "%s: cannot disassemble map", mpp->alias);
+               free(params);
                return DMP_ERR;
        }
 
-       *params = '\0';
-       if (dm_get_status(mpp->alias, params) != DMP_OK)
+       free(params);
+       params = NULL;
+       if (dm_get_status(mpp->alias, &params) != DMP_OK)
                condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
        else if (disassemble_status(params, mpp))
                condlog(2, "%s: cannot disassemble status", mpp->alias);
+       free(params);
 
        /* FIXME: we should deal with the return value here */
        update_pathvec_from_dm(pathvec, mpp, flags);
@@ -701,7 +704,7 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
        return mpp;
 
 out:
-       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec);
        return NULL;
 }
 
index ee2b723b10d2c7b7ef1e14648050746463ae0c5d..29ede454fff8a0bc01c709839f149cf22c00cccc 100644 (file)
@@ -27,15 +27,8 @@ int update_mpp_paths(struct multipath * mpp, vector pathvec);
 int update_multipath_strings (struct multipath *mpp, vector pathvec);
 void extract_hwe_from_path(struct multipath * mpp);
 
-enum {
-       KEEP_VEC,
-       PURGE_VEC,
-};
-
-void remove_map (struct multipath *mpp, vector pathvec, vector mpvec,
-                int purge_vec);
-void remove_map_by_alias(const char *alias, struct vectors * vecs,
-                        int purge_vec);
+void remove_map (struct multipath *mpp, vector pathvec, vector mpvec);
+void remove_map_by_alias(const char *alias, struct vectors * vecs);
 void remove_maps (struct vectors * vecs);
 
 void sync_map_state (struct multipath *);
index 7a2af1eaaf6908c289f88d7c75ce219f287ee88f..9ff145f26c9bf9f354edb87127eaaaa6f85b4ccf 100644 (file)
@@ -358,7 +358,7 @@ bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
                                        strchop(pp->wwid);
                                }
                        }
-                } else if (nr < 0)
+               } else if (nr < 0)
                        condlog(1, "%s: error reading from %s: %m",
                                __func__, pathbuf);
 
index d3061bf87ad524a0e3677675b5d8b661b43dab6b..4265904b6fd81fab9efd8ab90008ad2aabd92b49 100644 (file)
@@ -569,7 +569,7 @@ int uevent_listen(struct udev *udev)
        }
        pthread_cleanup_push(monitor_cleanup, monitor);
 #ifdef LIBUDEV_API_RECVBUF
-       if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024))
+       if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024) < 0)
                condlog(2, "failed to increase buffer size");
 #endif
        fd = udev_monitor_get_fd(monitor);
index 0e37f3ff01c96c5454cac14906eeb48430bcd677..ea858409cf66c107bc307c7a0c551454cf472001 100644 (file)
@@ -223,8 +223,8 @@ setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
 
        ret = pthread_attr_init(attr);
        assert(ret == 0);
-       if (stacksize < PTHREAD_STACK_MIN)
-               stacksize = PTHREAD_STACK_MIN;
+       if (PTHREAD_STACK_MIN > 0 && stacksize < (size_t)PTHREAD_STACK_MIN)
+               stacksize = (size_t)PTHREAD_STACK_MIN;
        ret = pthread_attr_setstacksize(attr, stacksize);
        assert(ret == 0);
        if (detached) {
@@ -455,3 +455,8 @@ int should_exit(void)
 {
        return 0;
 }
+
+void cleanup_charp(char **p)
+{
+       free(*p);
+}
index e9b48f9f3f53d8bcc9adec280c1a969e6dd4959f..89027f80588f0d5960934454d95735f36f0b26fc 100644 (file)
@@ -123,4 +123,5 @@ static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
                ___p;                  \
        })
 
+void cleanup_charp(char **p);
 #endif /* _UTIL_H */
index 6e68199f36f5d355668e77b9583773c34818facf..34e1d903fe2c5837e0c85b1b51e2f257077081a1 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000806
-#define DATE_CODE    0x040115
+#define VERSION_CODE 0x000807
+#define DATE_CODE    0x090815
 
 #define PROG    "multipath-tools"
 
index ef89c7cf86c6e973bb013aa263ebb08572badd89..65ece830cdf643e42c2ce37fa1402d5dd3d13467 100644 (file)
@@ -191,14 +191,14 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
                if (refwwid && strlen(refwwid) &&
                    strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
                        condlog(3, "skip map %s: out of scope", mpp->alias);
-                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
+                       remove_map(mpp, pathvec, curmp);
                        i--;
                        continue;
                }
 
                if (update_multipath_table(mpp, pathvec, flags) != DMP_OK) {
                        condlog(1, "error parsing map %s", mpp->wwid);
-                       remove_map(mpp, pathvec, curmp, PURGE_VEC);
+                       remove_map(mpp, pathvec, curmp);
                        i--;
                        continue;
                }
@@ -456,6 +456,7 @@ configure (struct config *conf, enum mpath_cmds cmd,
 {
        vector curmp = NULL;
        vector pathvec = NULL;
+       vector newmp = NULL;
        int r = RTVL_FAIL, rc;
        int di_flag = 0;
        char * refwwid = NULL;
@@ -466,9 +467,9 @@ configure (struct config *conf, enum mpath_cmds cmd,
         */
        curmp = vector_alloc();
        pathvec = vector_alloc();
-       atexit(cleanup_vecs);
+       newmp = vector_alloc();
 
-       if (!curmp || !pathvec) {
+       if (!curmp || !pathvec || !newmp) {
                condlog(0, "can not allocate memory");
                goto out;
        }
@@ -570,14 +571,27 @@ configure (struct config *conf, enum mpath_cmds cmd,
        /*
         * core logic entry point
         */
-       rc = coalesce_paths(&vecs, NULL, refwwid,
+       rc = coalesce_paths(&vecs, newmp, refwwid,
                           conf->force_reload, cmd);
        r = rc == CP_RETRY ? RTVL_RETRY : rc == CP_OK ? RTVL_OK : RTVL_FAIL;
 
 out:
+       if (r == RTVL_OK &&
+           (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG ||
+            cmd == CMD_CREATE) &&
+           (VECTOR_SIZE(curmp) > 0 || VECTOR_SIZE(newmp) > 0) &&
+           !check_daemon())
+               condlog(2, "Warning: multipath devices exist, but multipathd service is not running");
+
        if (refwwid)
                FREE(refwwid);
 
+       free_multipathvec(curmp, KEEP_PATHS);
+       vecs.mpvec = NULL;
+       free_multipathvec(newmp, KEEP_PATHS);
+       free_pathvec(pathvec, FREE_PATHS);
+       vecs.pathvec = NULL;
+
        return r;
 }
 
@@ -823,6 +837,7 @@ main (int argc, char *argv[])
        conf = get_multipath_config();
        conf->retrigger_tries = 0;
        conf->force_sync = 1;
+       atexit(cleanup_vecs);
        while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
                switch(arg) {
                case 1: printf("optarg : %s\n",optarg);
index 5b29a5d99e03c6ad30f3161dabcd5c4e1a5f6bcc..17df59f5f18d7ab0d0965868cd7aebd081b0188b 100644 (file)
@@ -225,7 +225,7 @@ Dry run, do not create or update devmaps.
 .TP
 .B \-e
 Enable all foreign libraries. This overrides the
-.I enable_foreign 
+.I enable_foreign
 option from \fBmultipath.conf(5)\fR.
 .
 .TP
index 064e482612afaaa152219944a2738a7a5a2613f7..d6b8c7f674e3eae67e2317b61b09f390fba8003c 100644 (file)
@@ -1251,7 +1251,7 @@ The default is: in \fB/sys/block/<dev>/queue/max_sectors_kb\fR
 Sets the number of seconds that multipath will wait after creating a device
 with only ghost paths before marking it ready for use in systemd. This gives
 the active paths time to appear before the multipath runs the hardware handler
-to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIon\fR
+to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIno\fR
 makes multipath immediately mark a device with only ghost paths as ready.
 .RS
 .TP
index d053c1edaa79d62cd0dd44a3590a2d94c7f4952d..393b6cbb275e91c45e3ab386646a0a4b703f79d6 100644 (file)
@@ -16,6 +16,8 @@ LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
           -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
           -ldevmapper -lreadline
+CFLAGS += $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
+       awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
 
 ifdef SYSTEMD
        CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
index bdc9fb10a36293c09d5bc5d25b732844bc030566..4d6c37c98523acef2d080050a0727a682da7adba 100644 (file)
@@ -16,6 +16,7 @@
 #include "mpath_cmd.h"
 #include "cli.h"
 #include "debug.h"
+#include "strbuf.h"
 
 static vector keys;
 static vector handlers;
@@ -354,107 +355,80 @@ alloc_handlers (void)
 }
 
 static int
-genhelp_sprint_aliases (char * reply, int maxlen, vector keys,
+genhelp_sprint_aliases (struct strbuf *reply, vector keys,
                        struct key * refkw)
 {
-       int i, len = 0;
+       int i;
        struct key * kw;
+       size_t initial_len = get_strbuf_len(reply);
 
        vector_foreach_slot (keys, kw, i) {
-               if (kw->code == refkw->code && kw != refkw) {
-                       len += snprintf(reply + len, maxlen - len,
-                                       "|%s", kw->str);
-                       if (len >= maxlen)
-                               return len;
-               }
+               if (kw->code == refkw->code && kw != refkw &&
+                   print_strbuf(reply, "|%s", kw->str) < 0)
+                       return -1;
        }
 
-       return len;
+       return get_strbuf_len(reply) - initial_len;
 }
 
 static int
-do_genhelp(char *reply, int maxlen, const char *cmd, int error) {
-       int len = 0;
+do_genhelp(struct strbuf *reply, const char *cmd, int error) {
        int i, j;
        uint64_t fp;
        struct handler * h;
        struct key * kw;
+       int rc = 0;
+       size_t initial_len = get_strbuf_len(reply);
 
        switch(error) {
        case ENOMEM:
-               len += snprintf(reply + len, maxlen - len,
-                               "%s: Not enough memory\n", cmd);
+               rc = print_strbuf(reply, "%s: Not enough memory\n", cmd);
                break;
        case EAGAIN:
-               len += snprintf(reply + len, maxlen - len,
-                               "%s: not found\n", cmd);
+               rc = print_strbuf(reply, "%s: not found\n", cmd);
                break;
        case EINVAL:
-               len += snprintf(reply + len, maxlen - len,
-                               "%s: Missing argument\n", cmd);
+               rc = print_strbuf(reply, "%s: Missing argument\n", cmd);
                break;
        }
-       if (len >= maxlen)
-               goto out;
-       len += snprintf(reply + len, maxlen - len, VERSION_STRING);
-       if (len >= maxlen)
-               goto out;
-       len += snprintf(reply + len, maxlen - len, "CLI commands reference:\n");
-       if (len >= maxlen)
-               goto out;
+       if (rc < 0)
+               return -1;
+
+       if (print_strbuf(reply, VERSION_STRING) < 0 ||
+           append_strbuf_str(reply, "CLI commands reference:\n") < 0)
+               return -1;
 
        vector_foreach_slot (handlers, h, i) {
                fp = h->fingerprint;
                vector_foreach_slot (keys, kw, j) {
                        if ((kw->code & fp)) {
                                fp -= kw->code;
-                               len += snprintf(reply + len , maxlen - len,
-                                               " %s", kw->str);
-                               if (len >= maxlen)
-                                       goto out;
-                               len += genhelp_sprint_aliases(reply + len,
-                                                             maxlen - len,
-                                                             keys, kw);
-                               if (len >= maxlen)
-                                       goto out;
+                               if (print_strbuf(reply, " %s", kw->str) < 0 ||
+                                   genhelp_sprint_aliases(reply, keys, kw) < 0)
+                                       return -1;
 
                                if (kw->has_param) {
-                                       len += snprintf(reply + len,
-                                                       maxlen - len,
-                                                       " $%s", kw->str);
-                                       if (len >= maxlen)
-                                               goto out;
+                                       if (print_strbuf(reply, " $%s",
+                                                        kw->str) < 0)
+                                               return -1;
                                }
                        }
                }
-               len += snprintf(reply + len, maxlen - len, "\n");
-               if (len >= maxlen)
-                       goto out;
+               if (append_strbuf_str(reply, "\n") < 0)
+                       return -1;
        }
-out:
-       return len;
+       return get_strbuf_len(reply) - initial_len;
 }
 
 
 static char *
 genhelp_handler (const char *cmd, int error)
 {
-       char * reply;
-       char * p = NULL;
-       int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
-
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return NULL;
-               p = reply;
-               p += do_genhelp(reply, maxlen, cmd, error);
-               again = ((p - reply) >= maxlen);
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       return reply;
+       STRBUF_ON_STACK(reply);
+
+       if (do_genhelp(&reply, cmd, error) == -1)
+               condlog(0, "genhelp_handler: out of memory");
+       return steal_strbuf_str(&reply);
 }
 
 int
index 1de6ad8ede7dbdeab946adf187b3580a5f2fc67c..6d3a0ae2ecd0a1e4b53c9191e9209117dfc682d6 100644 (file)
 #include "cli.h"
 #include "uevent.h"
 #include "foreign.h"
+#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)
 {
+       STRBUF_ON_STACK(reply);
        int i;
        struct path * pp;
-       char * c;
-       char * reply, * header;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       int hdr_len = 0;
 
        get_path_layout(vecs->pathvec, 1);
        foreign_path_layout();
 
-       reply = MALLOC(maxlen);
+       if (pretty && (hdr_len = snprint_path_header(&reply, style)) < 0)
+               return 1;
 
-       while (again) {
-               if (!reply)
+       vector_foreach_slot(vecs->pathvec, pp, i) {
+               if (snprint_path(&reply, style, pp, pretty) < 0)
                        return 1;
-
-               c = reply;
-
-               if (pretty)
-                       c += snprint_path_header(c, reply + maxlen - c,
-                                                style);
-               header = c;
-
-               vector_foreach_slot(vecs->pathvec, pp, i)
-                       c += snprint_path(c, reply + maxlen - c,
-                                         style, pp, pretty);
-
-               c += snprint_foreign_paths(c, reply + maxlen - c,
-                                          style, pretty);
-
-               again = (c == reply + maxlen - 1);
-
-               REALLOC_REPLY(reply, again, maxlen);
        }
+       if (snprint_foreign_paths(&reply, style, pretty) < 0)
+               return 1;
 
-       if (pretty && c == header) {
+       if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
                /* No output - clear header */
-               *reply = '\0';
-               c = reply;
-       }
+               truncate_strbuf(&reply, 0);
 
-       *r = reply;
-       *len = (int)(c - reply + 1);
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -86,28 +74,14 @@ int
 show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
           char * style)
 {
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       STRBUF_ON_STACK(reply);
 
        get_path_layout(vecs->pathvec, 1);
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-
-               c += snprint_path(c, reply + maxlen - c, style, pp, 0);
-
-               again = (c == reply + maxlen - 1);
+       if (snprint_path(&reply, style, pp, 0) < 0)
+               return 1;
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
 
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       *r = reply;
-       *len = (int)(c - reply + 1);
        return 0;
 }
 
@@ -115,84 +89,51 @@ int
 show_map_topology (char ** r, int * len, struct multipath * mpp,
                   struct vectors * vecs)
 {
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       STRBUF_ON_STACK(reply);
 
        if (update_multipath(vecs, mpp->alias, 0))
                return 1;
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
 
-               c = reply;
-
-               c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
-               again = (c == reply + maxlen - 1);
+       if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+               return 1;
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
 
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       *r = reply;
-       *len = (int)(c - reply + 1);
        return 0;
 }
 
 int
 show_maps_topology (char ** r, int * len, struct vectors * vecs)
 {
+       STRBUF_ON_STACK(reply);
        int i;
        struct multipath * mpp;
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
 
        get_path_layout(vecs->pathvec, 0);
        foreign_path_layout();
 
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-
-               vector_foreach_slot(vecs->mpvec, mpp, i) {
-                       if (update_multipath(vecs, mpp->alias, 0)) {
-                               i--;
-                               continue;
-                       }
-                       c += snprint_multipath_topology(c, reply + maxlen - c,
-                                                       mpp, 2);
+       vector_foreach_slot(vecs->mpvec, mpp, i) {
+               if (update_multipath(vecs, mpp->alias, 0)) {
+                       i--;
+                       continue;
                }
-               c += snprint_foreign_topology(c, reply + maxlen - c, 2);
-
-               again = (c == reply + maxlen - 1);
-
-               REALLOC_REPLY(reply, again, maxlen);
+               if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+                       return 1;
        }
+       if (snprint_foreign_topology(&reply, 2) < 0)
+               return 1;
 
-       *r = reply;
-       *len = (int)(c - reply + 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)
 {
+       STRBUF_ON_STACK(reply);
        int i;
        struct multipath * mpp;
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
-
-       if (VECTOR_SIZE(vecs->mpvec) > 0)
-               maxlen *= PRINT_JSON_MULTIPLIER * VECTOR_SIZE(vecs->mpvec);
 
        vector_foreach_slot(vecs->mpvec, mpp, i) {
                if (update_multipath(vecs, mpp->alias, 0)) {
@@ -200,21 +141,11 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
                }
        }
 
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-
-               c += snprint_multipath_topology_json(c, maxlen, vecs);
-               again = (c == reply + maxlen);
+       if (snprint_multipath_topology_json(&reply, vecs) < 0)
+               return 1;
 
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       *r = reply;
-       *len = (int)(c - reply);
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -222,28 +153,16 @@ int
 show_map_json (char ** r, int * len, struct multipath * mpp,
                   struct vectors * vecs)
 {
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       STRBUF_ON_STACK(reply);
 
        if (update_multipath(vecs, mpp->alias, 0))
                return 1;
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
 
-               c = reply;
-
-               c += snprint_multipath_map_json(c, maxlen, mpp);
-               again = (c == reply + maxlen);
+       if (snprint_multipath_map_json(&reply, mpp) < 0)
+               return 1;
 
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       *r = reply;
-       *len = (int)(c - reply);
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -414,58 +333,40 @@ cli_list_maps_json (void * v, char ** reply, int * len, void * data)
 int
 cli_list_wildcards (void * v, char ** reply, int * len, void * data)
 {
-       char * c;
-
-       *reply = MALLOC(INITIAL_REPLY_LEN);
+       STRBUF_ON_STACK(buf);
 
-       if (!*reply)
+       if (snprint_wildcards(&buf) < 0)
                return 1;
 
-       c = *reply;
-       c += snprint_wildcards(c, INITIAL_REPLY_LEN);
-
-       *len = INITIAL_REPLY_LEN;
+       *len = get_strbuf_len(&buf) + 1;
+       *reply = steal_strbuf_str(&buf);
        return 0;
 }
 
 int
 show_status (char ** r, int *len, struct vectors * vecs)
 {
-       char * c;
-       char * reply;
-
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       reply = MALLOC(maxlen);
+       STRBUF_ON_STACK(reply);
 
-       if (!reply)
+       if (snprint_status(&reply, vecs) < 0)
                return 1;
 
-       c = reply;
-       c += snprint_status(c, reply + maxlen - c, vecs);
-
-       *r = reply;
-       *len = (int)(c - reply + 1);
+       *len = get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
 int
 show_daemon (char ** r, int *len)
 {
-       char * c;
-       char * reply;
-
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       reply = MALLOC(maxlen);
+       STRBUF_ON_STACK(reply);
 
-       if (!reply)
+       if (print_strbuf(&reply, "pid %d %s\n",
+                        daemon_pid, daemon_status()) < 0)
                return 1;
 
-       c = reply;
-       c += snprintf(c, INITIAL_REPLY_LEN, "pid %d %s\n",
-                     daemon_pid, daemon_status());
-
-       *r = reply;
-       *len = (int)(c - reply + 1);
+       *len = get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -473,26 +374,13 @@ int
 show_map (char ** r, int *len, struct multipath * mpp, char * style,
          int pretty)
 {
-       char * c;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
-
-       reply = MALLOC(maxlen);
-       while (again) {
-               if (!reply)
-                       return 1;
+       STRBUF_ON_STACK(reply);
 
-               c = reply;
-               c += snprint_multipath(c, reply + maxlen - c, style,
-                                      mpp, pretty);
-
-               again = (c == reply + maxlen - 1);
+       if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+               return 1;
 
-               REALLOC_REPLY(reply, again, maxlen);
-       }
-       *r = reply;
-       *len = (int)(c - reply + 1);
+       *len = get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -500,51 +388,34 @@ int
 show_maps (char ** r, int *len, struct vectors * vecs, char * style,
           int pretty)
 {
+       STRBUF_ON_STACK(reply);
        int i;
        struct multipath * mpp;
-       char * c, *header;
-       char * reply;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       int hdr_len = 0;
 
        get_multipath_layout(vecs->mpvec, 1);
        foreign_multipath_layout();
 
-       reply = MALLOC(maxlen);
-
-       while (again) {
-               if (!reply)
-                       return 1;
-
-               c = reply;
-               if (pretty)
-                       c += snprint_multipath_header(c, reply + maxlen - c,
-                                                     style);
-               header = c;
-
-               vector_foreach_slot(vecs->mpvec, mpp, i) {
-                       if (update_multipath(vecs, mpp->alias, 0)) {
-                               i--;
-                               continue;
-                       }
-                       c += snprint_multipath(c, reply + maxlen - c,
-                                              style, mpp, pretty);
+       if (pretty && (hdr_len = snprint_multipath_header(&reply, style)) < 0)
+               return 1;
 
+       vector_foreach_slot(vecs->mpvec, mpp, i) {
+               if (update_multipath(vecs, mpp->alias, 0)) {
+                       i--;
+                       continue;
                }
-               c += snprint_foreign_multipaths(c, reply + maxlen - c,
-                                               style, pretty);
-               again = (c == reply + maxlen - 1);
-
-               REALLOC_REPLY(reply, again, maxlen);
+               if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+                       return 1;
        }
+       if (snprint_foreign_multipaths(&reply, style, pretty) < 0)
+               return 1;
 
-       if (pretty && c == header) {
+       if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
                /* No output - clear header */
-               *reply = '\0';
-               c = reply;
-       }
-       *r = reply;
-       *len = (int)(c - reply + 1);
+               truncate_strbuf(&reply, 0);
+
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -752,7 +623,8 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
                                /* Have the checker reinstate this path asap */
                                pp->tick = 1;
                                return 0;
-                       } else if (!ev_remove_path(pp, vecs, true))
+                       } else if (ev_remove_path(pp, vecs, true) &
+                                  REMOVE_PATH_SUCCESS)
                                /* Path removed in ev_remove_path() */
                                pp = NULL;
                        else {
@@ -801,8 +673,7 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
        }
        return ev_add_path(pp, vecs, 1);
 blacklisted:
-       *reply = strdup("blacklisted\n");
-       *len = strlen(*reply) + 1;
+       SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
        condlog(2, "%s: path blacklisted", param);
        return 0;
 }
@@ -813,6 +684,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, PATH);
        struct path *pp;
+       int ret;
 
        param = convert_dev(param, 1);
        condlog(2, "%s: remove path (operator)", param);
@@ -821,7 +693,12 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
                condlog(0, "%s: path already removed", param);
                return 1;
        }
-       return ev_remove_path(pp, vecs, 1);
+       ret = ev_remove_path(pp, vecs, 1);
+       if (ret == REMOVE_PATH_DELAY)
+               SET_REPLY_AND_LEN(reply, len, "delayed\n");
+       else if (ret == REMOVE_PATH_MAP_ERROR)
+               SET_REPLY_AND_LEN(reply, len, "map reload error. removed\n");
+       return (ret == REMOVE_PATH_FAILURE);
 }
 
 int
@@ -845,8 +722,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
                invalid = 1;
        pthread_cleanup_pop(1);
        if (invalid) {
-               *reply = strdup("blacklisted\n");
-               *len = strlen(*reply) + 1;
+               SET_REPLY_AND_LEN(reply, len, "blacklisted\n");
                condlog(2, "%s: map blacklisted", param);
                return 1;
        }
@@ -961,12 +837,12 @@ cli_reload(void *v, char **reply, int *len, void *data)
 int resize_map(struct multipath *mpp, unsigned long long size,
               struct vectors * vecs)
 {
-       char params[PARAMS_SIZE] = {0};
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
        unsigned long long orig_size = mpp->size;
 
        mpp->size = size;
        update_mpp_paths(mpp, vecs->pathvec);
-       if (setup_map(mpp, params, PARAMS_SIZE, vecs) != 0) {
+       if (setup_map(mpp, &params, vecs) != 0) {
                condlog(0, "%s: failed to setup map for resize : %s",
                        mpp->alias, strerror(errno));
                mpp->size = orig_size;
@@ -1204,7 +1080,7 @@ cli_reconfigure(void * v, char ** reply, int * len, void * data)
 
        condlog(2, "reconfigure (operator)");
 
-       rc = set_config_state(DAEMON_CONFIGURE); 
+       rc = set_config_state(DAEMON_CONFIGURE);
        if (rc == ETIMEDOUT) {
                condlog(2, "timeout starting reconfiguration");
                return 1;
@@ -1355,34 +1231,20 @@ cli_fail(void * v, char ** reply, int * len, void * data)
 int
 show_blacklist (char ** r, int * len)
 {
-       char *c = NULL;
-       char *reply = NULL;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       STRBUF_ON_STACK(reply);
        struct config *conf;
-       int fail = 0;
-
-       reply = MALLOC(maxlen);
+       bool fail;
 
        conf = get_multipath_config();
        pthread_cleanup_push(put_multipath_config, conf);
-       while (again) {
-               if (!reply) {
-                       fail = 1;
-                       break;
-               }
-
-               c = reply;
-               c += snprint_blacklist_report(conf, c, maxlen);
-               again = (c == reply + maxlen);
-               REALLOC_REPLY(reply, again, maxlen);
-       }
+       fail = snprint_blacklist_report(conf, &reply) < 0;
        pthread_cleanup_pop(1);
 
        if (fail)
                return 1;
-       *r = reply;
-       *len = (int)(c - reply + 1);
+
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
        return 0;
 }
 
@@ -1397,34 +1259,20 @@ cli_list_blacklist (void * v, char ** reply, int * len, void * data)
 int
 show_devices (char ** r, int * len, struct vectors *vecs)
 {
-       char *c = NULL;
-       char *reply = NULL;
-       unsigned int maxlen = INITIAL_REPLY_LEN;
-       int again = 1;
+       STRBUF_ON_STACK(reply);
        struct config *conf;
-       int fail = 0;
-
-       reply = MALLOC(maxlen);
+       bool fail;
 
        conf = get_multipath_config();
        pthread_cleanup_push(put_multipath_config, conf);
-       while (again) {
-               if (!reply) {
-                       fail = 1;
-                       break;
-               }
-
-               c = reply;
-               c += snprint_devices(conf, c, maxlen, vecs);
-               again = (c == reply + maxlen);
-               REALLOC_REPLY(reply, again, maxlen);
-       }
+       fail = snprint_devices(conf, &reply, vecs) < 0;
        pthread_cleanup_pop(1);
 
        if (fail)
                return 1;
-       *r = reply;
-       *len = (int)(c - reply + 1);
+
+       *len = (int)get_strbuf_len(&reply) + 1;
+       *r = steal_strbuf_str(&reply);
 
        return 0;
 }
@@ -1529,7 +1377,7 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
        struct multipath * mpp;
        struct vectors * vecs = (struct vectors *)data;
        char *mapname = get_keyparam(v, MAP);
-       char *flagstr = "";
+       uint64_t key;
 
        mapname = convert_dev(mapname, 0);
        condlog(3, "%s: get persistent reservation key (operator)", mapname);
@@ -1542,17 +1390,16 @@ cli_getprkey(void * v, char ** reply, int * len, void * data)
        if (!*reply)
                return 1;
 
-       if (!get_be64(mpp->reservation_key)) {
+       key = get_be64(mpp->reservation_key);
+       if (!key) {
                sprintf(*reply, "none\n");
-               *len = strlen(*reply) + 1;
+               *len = sizeof("none\n");
                return 0;
        }
-       if (mpp->sa_flags & MPATH_F_APTPL_MASK)
-               flagstr = ":aptpl";
-       snprintf(*reply, 26, "0x%" PRIx64 "%s\n",
-                get_be64(mpp->reservation_key), flagstr);
-       (*reply)[19] = '\0';
-       *len = strlen(*reply) + 1;
+
+       /* 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;
        return 0;
 }
 
index f52f59705168bf23152f17cc30d0755e6e0b6df2..f035ee7f66cc9580f6a505f3ca9a72a7f885e67f 100644 (file)
@@ -359,7 +359,7 @@ static int dmevent_loop (void)
                pthread_testcancel();
                r = 0;
                if (curr_dev.action == EVENT_REMOVE)
-                       remove_map_by_alias(curr_dev.name, waiter->vecs, PURGE_VEC);
+                       remove_map_by_alias(curr_dev.name, waiter->vecs);
                else
                        r = update_multipath(waiter->vecs, curr_dev.name, 1);
                pthread_cleanup_pop(1);
index 102946bf2286cba39ce7cc426f2bcbf51c39c8c8..3aff241d8caffe66f267026618cb446d6a91f7ce 100644 (file)
@@ -385,7 +385,7 @@ remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs)
        condlog(3, "%s: removing map from internal tables", mpp->alias);
        if (!poll_dmevents)
                stop_waiter_thread(mpp);
-       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec);
 }
 
 static void
@@ -489,7 +489,7 @@ static int
 update_map (struct multipath *mpp, struct vectors *vecs, int new_map)
 {
        int retries = 3;
-       char params[PARAMS_SIZE] = {0};
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
 
 retry:
        condlog(4, "%s: updating new map", mpp->alias);
@@ -502,13 +502,15 @@ retry:
        verify_paths(mpp);
        mpp->action = ACT_RELOAD;
 
-       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+       if (setup_map(mpp, &params, vecs)) {
                condlog(0, "%s: failed to setup new map in update", mpp->alias);
                retries = -1;
                goto fail;
        }
        if (domap(mpp, params, 1) == DOMAP_FAIL && retries-- > 0) {
                condlog(0, "%s: map_udate sleep", mpp->alias);
+               free(params);
+               params = NULL;
                sleep(1);
                goto retry;
        }
@@ -516,7 +518,7 @@ retry:
 fail:
        if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) {
                condlog(0, "%s: failed to create new map", mpp->alias);
-               remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+               remove_map(mpp, vecs->pathvec, vecs->mpvec);
                return 1;
        }
 
@@ -570,7 +572,7 @@ add_map_without_path (struct vectors *vecs, const char *alias)
 
        return mpp;
 out:
-       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec);
        return NULL;
 }
 
@@ -660,7 +662,6 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
        else
                condlog(2, "%s: map flushed", mpp->alias);
 
-       orphan_paths(vecs->pathvec, mpp, "map flushed");
        remove_map_and_stop_waiter(mpp, vecs);
 
        return 0;
@@ -839,7 +840,7 @@ handle_path_wwid_change(struct path *pp, struct vectors *vecs)
                return;
 
        udd = udev_device_ref(pp->udev);
-       if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) {
+       if (!(ev_remove_path(pp, vecs, 1) & REMOVE_PATH_SUCCESS) && pp->mpp) {
                pp->dmstate = PSTATE_FAILED;
                dm_fail_path(pp->mpp->alias, pp->dev_t);
        }
@@ -949,8 +950,8 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                                 * Make another attempt to remove the path
                                 */
                                pp->mpp = prev_mpp;
-                               ret = ev_remove_path(pp, vecs, true);
-                               if (ret != 0) {
+                               if (!(ev_remove_path(pp, vecs, true) &
+                                     REMOVE_PATH_SUCCESS)) {
                                        /*
                                         * Failure in ev_remove_path will keep
                                         * path in pathvec in INIT_REMOVED state
@@ -961,6 +962,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                                        dm_fail_path(pp->mpp->alias, pp->dev_t);
                                        condlog(1, "%s: failed to re-add path still mapped in %s",
                                                pp->dev, pp->mpp->alias);
+                                       ret = 1;
                                } else if (r == PATHINFO_OK)
                                        /*
                                         * Path successfully freed, move on to
@@ -1028,7 +1030,7 @@ int
 ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)
 {
        struct multipath * mpp;
-       char params[PARAMS_SIZE] = {0};
+       char *params __attribute((cleanup(cleanup_charp))) = NULL;
        int retries = 3;
        int start_waiter = 0;
        int ret;
@@ -1104,7 +1106,7 @@ rescan:
        /*
         * push the map to the device-mapper
         */
-       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+       if (setup_map(mpp, &params, vecs)) {
                condlog(0, "%s: failed to setup map for addition of new "
                        "path %s", mpp->alias, pp->dev);
                goto fail_map;
@@ -1129,6 +1131,8 @@ rescan:
                        condlog(0, "%s: ev_add_path sleep", mpp->alias);
                        sleep(1);
                        update_mpp_paths(mpp, vecs->pathvec);
+                       free(params);
+                       params = NULL;
                        goto rescan;
                }
                else if (mpp->action == ACT_RELOAD)
@@ -1158,7 +1162,7 @@ rescan:
                goto fail;
 
 fail_map:
-       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+       remove_map(mpp, vecs->pathvec, vecs->mpvec);
 fail:
        orphan_path(pp, "failed to add path");
        return 1;
@@ -1168,7 +1172,6 @@ static int
 uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 {
        struct path *pp;
-       int ret;
 
        condlog(3, "%s: remove path (uevent)", uev->kernel);
        delete_foreign(uev->udev);
@@ -1178,27 +1181,31 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
        pthread_testcancel();
        pp = find_path_by_dev(vecs->pathvec, uev->kernel);
        if (pp)
-               ret = ev_remove_path(pp, vecs, need_do_map);
+               ev_remove_path(pp, vecs, need_do_map);
        lock_cleanup_pop(vecs->lock);
-       if (!pp) {
-               /* Not an error; path might have been purged earlier */
+       if (!pp) /* Not an error; path might have been purged earlier */
                condlog(0, "%s: path already removed", uev->kernel);
-               return 0;
-       }
-       return ret;
+       return 0;
 }
 
 int
 ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
 {
        struct multipath * mpp;
-       int i, retval = 0;
-       char params[PARAMS_SIZE] = {0};
+       int i, retval = REMOVE_PATH_SUCCESS;
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
 
        /*
         * avoid referring to the map of an orphaned path
         */
        if ((mpp = pp->mpp)) {
+               /*
+                * Mark the path as removed. In case of success, we
+                * will delete it for good. Otherwise, it will be deleted
+                * later, unless all attempts to reload this map fail.
+                */
+               set_path_removed(pp);
+
                /*
                 * transform the mp->pg vector of vectors of paths
                 * into a mp->params string to feed the device-mapper
@@ -1210,13 +1217,9 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                }
 
                /*
-                * Mark the path as removed. In case of success, we
-                * will delete it for good. Otherwise, it will be deleted
-                * later, unless all attempts to reload this map fail.
-                * Note: we have to explicitly remove pp from mpp->paths,
+                * we have to explicitly remove pp from mpp->paths,
                 * update_mpp_paths() doesn't do that.
                 */
-               set_path_removed(pp);
                i = find_slot(mpp->paths, pp);
                if (i != -1)
                        vector_del_slot(mpp->paths, i);
@@ -1243,7 +1246,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                                condlog(2, "%s: removed map after"
                                        " removing all paths",
                                        alias);
-                               retval = 0;
                                /* flush_map() has freed the path */
                                goto out;
                        }
@@ -1252,7 +1254,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                         */
                }
 
-               if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+               if (setup_map(mpp, &params, vecs)) {
                        condlog(0, "%s: failed to setup map for"
                                " removal of path %s", mpp->alias, pp->dev);
                        goto fail;
@@ -1260,11 +1262,14 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
 
                if (mpp->wait_for_udev) {
                        mpp->wait_for_udev = 2;
+                       retval = REMOVE_PATH_DELAY;
                        goto out;
                }
 
-               if (!need_do_map)
+               if (!need_do_map) {
+                       retval = REMOVE_PATH_DELAY;
                        goto out;
+               }
                /*
                 * reload the map
                 */
@@ -1273,7 +1278,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                        condlog(0, "%s: failed in domap for "
                                "removal of path %s",
                                mpp->alias, pp->dev);
-                       retval = 1;
+                       retval = REMOVE_PATH_FAILURE;
                } else {
                        /*
                         * update our state from kernel
@@ -1281,12 +1286,12 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                        char devt[BLK_DEV_SIZE];
 
                        strlcpy(devt, pp->dev_t, sizeof(devt));
+
+                       /* setup_multipath will free the path
+                        * regardless of whether it succeeds or
+                        * fails */
                        if (setup_multipath(vecs, mpp))
-                               return 1;
-                       /*
-                        * Successful map reload without this path:
-                        * sync_map_state() will free it.
-                        */
+                               return REMOVE_PATH_MAP_ERROR;
                        sync_map_state(mpp);
 
                        condlog(2, "%s: path removed from map %s",
@@ -1302,8 +1307,10 @@ out:
        return retval;
 
 fail:
+       condlog(0, "%s: error removing path. removing map %s", pp->dev,
+               mpp->alias);
        remove_map_and_stop_waiter(mpp, vecs);
-       return 1;
+       return REMOVE_PATH_MAP_ERROR;
 }
 
 static int
@@ -1356,7 +1363,6 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                        condlog(0, "%s: path wwid changed from '%s' to '%s'",
                                uev->kernel, wwid, pp->wwid);
                        ev_remove_path(pp, vecs, 1);
-                       rescan_path(uev->udev);
                        needs_reinit = 1;
                        goto out;
                } else {
@@ -1463,7 +1469,7 @@ map_discovery (struct vectors * vecs)
 
        vector_foreach_slot (vecs->mpvec, mpp, i)
                if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK) {
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec);
                        i--;
                }
 
@@ -1938,7 +1944,7 @@ int update_prio(struct path *pp, int refresh_all)
 static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
                      int is_daemon)
 {
-       char params[PARAMS_SIZE] = {0};
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
        struct path *pp;
        int i, r;
 
@@ -1956,7 +1962,7 @@ static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
                        }
                }
        }
-       if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+       if (setup_map(mpp, &params, vecs)) {
                condlog(0, "%s: failed to setup map", mpp->alias);
                return 1;
        }
@@ -2012,7 +2018,7 @@ static int check_path_reinstate_state(struct path * pp) {
 
                /* If path became failed again or continue failed, should reset
                 * path san_path_err_forget_rate and path dis_reinstate_time to
-                * start a new stable check. 
+                * start a new stable check.
                 */
                if ((pp->state != PATH_UP) && (pp->state != PATH_GHOST) &&
                        (pp->state != PATH_DELAYED)) {
@@ -2700,7 +2706,7 @@ configure (struct vectors * vecs)
         */
        vector_foreach_slot(vecs->mpvec, mpp, i) {
                if (wait_for_events(mpp, vecs)) {
-                       remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec);
                        i--;
                        continue;
                }
@@ -3031,6 +3037,10 @@ static void cleanup_threads(void)
        pthread_attr_destroy(&waiter_attr);
 }
 
+#ifndef URCU_VERSION
+#  define URCU_VERSION 0
+#endif
+#if (URCU_VERSION >= 0x000800)
 /*
  * Use a non-default call_rcu_data for child().
  *
@@ -3040,6 +3050,9 @@ static void cleanup_threads(void)
  * can't be joined with pthread_join(), leaving a memory leak.
  *
  * Therefore we create our own, which can be destroyed and joined.
+ * The cleanup handler needs to call rcu_barrier(), which is only
+ * available in user-space RCU v0.8 and newer. See
+ * https://lists.lttng.org/pipermail/lttng-dev/2021-May/029958.html
  */
 static struct call_rcu_data *setup_rcu(void)
 {
@@ -3072,6 +3085,7 @@ static void cleanup_rcu(void)
        }
        rcu_unregister_thread();
 }
+#endif /* URCU_VERSION */
 
 static void cleanup_child(void)
 {
@@ -3116,9 +3130,14 @@ child (__attribute__((unused)) void *param)
        init_unwinder();
        mlockall(MCL_CURRENT | MCL_FUTURE);
        signal_init();
+#if (URCU_VERSION >= 0x000800)
        mp_rcu_data = setup_rcu();
-
-       if (atexit(cleanup_rcu) || atexit(cleanup_child))
+       if (atexit(cleanup_rcu))
+               fprintf(stderr, "failed to register RCU cleanup handler\n");
+#else
+       rcu_init();
+#endif
+       if (atexit(cleanup_child))
                fprintf(stderr, "failed to register cleanup handlers\n");
 
        setup_thread_attr(&misc_attr, 64 * 1024, 0);
index ddd953f988461ba4bec2b67eb3ee280b2809689f..bc1f938f1dd20a9167100d15a32788b6752ad54f 100644 (file)
@@ -13,6 +13,20 @@ enum daemon_status {
        DAEMON_STATUS_SIZE,
 };
 
+enum remove_path_result {
+       REMOVE_PATH_FAILURE = 0x0, /* path could not be removed. It is still
+                                   * part of the kernel map, but its state
+                                   * is set to INIT_REMOVED, and it will be
+                                   * removed at the next possible occassion */
+       REMOVE_PATH_SUCCESS = 0x1, /* path was removed */
+       REMOVE_PATH_DELAY = 0x2, /* path is set to be removed later. it
+                                 * currently still exists and is part of the
+                                 * kernel map */
+       REMOVE_PATH_MAP_ERROR = 0x5, /* map was removed because of error. value
+                                     * includes REMOVE_PATH_SUCCESS bit
+                                     * because the path was also removed */
+};
+
 struct prout_param_descriptor;
 struct prin_resp;
 
index 7d547fa76f8ee50673c936099e7bc6743501546b..0b2ac8147108c9de2701f4df104d86529be4bfcd 100644 (file)
@@ -8,6 +8,7 @@ DefaultDependencies=no
 Conflicts=shutdown.target
 ConditionKernelCommandLine=!nompath
 ConditionKernelCommandLine=!multipath=off
+ConditionVirtualization=!container
 
 [Service]
 Type=notify
index e70c8ed77e9ebaae23727053bcea0d09de26e1b6..8cbc4b73e497a30ef31ea59f827952bd04998799 100644 (file)
@@ -13,7 +13,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
 LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
-        alias directio valid devt mpathvalid
+        alias directio valid devt mpathvalid strbuf
 HELPERS := test-lib.o test-log.o
 
 .SILENT: $(TESTS:%=%.o)
@@ -63,6 +63,7 @@ mpathvalid-test_OBJDEPS := ../libmpathvalid/mpath_valid.o
 ifneq ($(DIO_TEST_DEV),)
 directio-test_LIBDEPS := -laio
 endif
+strbuf-test_OBJDEPS := ../libmultipath/strbuf.o
 
 %.o: %.c
        $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
index 7e7c1878da64e8175c1457887617434c45331bba..3ca6c28bbd7911a39f072b7617ef800ff0bf0488 100644 (file)
@@ -81,12 +81,25 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
        return ret;
 }
 
+/* strbuf wrapper for the old format_devname() */
+static int __format_devname(char *name, int id, size_t len, const char *prefix)
+{
+       STRBUF_ON_STACK(buf);
+
+       if (append_strbuf_str(&buf, prefix) < 0 ||
+           format_devname(&buf, id) < 0 ||
+           len <= get_strbuf_len(&buf))
+               return -1;
+       strcpy(name, get_strbuf_str(&buf));
+       return get_strbuf_len(&buf);
+}
+
 static void fd_mpatha(void **state)
 {
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 1, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 1, sizeof(buf), "FOO");
        assert_int_equal(rc, 4);
        assert_string_equal(buf, "FOOa");
 }
@@ -97,7 +110,7 @@ static void fd_mpathz(void **state)
        char buf[5];
        int rc;
 
-       rc = format_devname(buf, 26, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26, sizeof(buf), "FOO");
        assert_int_equal(rc, 4);
        assert_string_equal(buf, "FOOz");
 }
@@ -107,7 +120,7 @@ static void fd_mpathaa(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26 + 1, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26 + 1, sizeof(buf), "FOO");
        assert_int_equal(rc, 5);
        assert_string_equal(buf, "FOOaa");
 }
@@ -117,7 +130,7 @@ static void fd_mpathzz(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26*26 + 26, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26*26 + 26, sizeof(buf), "FOO");
        assert_int_equal(rc, 5);
        assert_string_equal(buf, "FOOzz");
 }
@@ -127,7 +140,7 @@ static void fd_mpathaaa(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26*26 + 27, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26*26 + 27, sizeof(buf), "FOO");
        assert_int_equal(rc, 6);
        assert_string_equal(buf, "FOOaaa");
 }
@@ -137,7 +150,7 @@ static void fd_mpathzzz(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26*26*26 + 26*26 + 26, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26*26*26 + 26*26 + 26, sizeof(buf), "FOO");
        assert_int_equal(rc, 6);
        assert_string_equal(buf, "FOOzzz");
 }
@@ -147,7 +160,7 @@ static void fd_mpathaaaa(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26*26*26 + 26*26 + 27, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 26*26*26 + 26*26 + 27, sizeof(buf), "FOO");
        assert_int_equal(rc, 7);
        assert_string_equal(buf, "FOOaaaa");
 }
@@ -157,7 +170,7 @@ static void fd_mpathzzzz(void **state)
        char buf[32];
        int rc;
 
-       rc = format_devname(buf, 26*26*26*26 + 26*26*26 + 26*26 + 26,
+       rc = __format_devname(buf, 26*26*26*26 + 26*26*26 + 26*26 + 26,
                            sizeof(buf), "FOO");
        assert_int_equal(rc, 7);
        assert_string_equal(buf, "FOOzzzz");
@@ -169,7 +182,7 @@ static void fd_mpath_max(void **state)
        char buf[32];
        int rc;
 
-       rc  = format_devname(buf, INT_MAX, sizeof(buf), "");
+       rc  = __format_devname(buf, INT_MAX, sizeof(buf), "");
        assert_int_equal(rc, strlen(MPATH_ID_INT_MAX));
        assert_string_equal(buf, MPATH_ID_INT_MAX);
 }
@@ -180,7 +193,7 @@ static void fd_mpath_max1(void **state)
        char buf[32];
        int rc;
 
-       rc  = format_devname(buf, INT_MIN, sizeof(buf), "");
+       rc  = __format_devname(buf, INT_MIN, sizeof(buf), "");
        assert_int_equal(rc, -1);
 }
 
@@ -189,7 +202,7 @@ static void fd_mpath_short(void **state)
        char buf[4];
        int rc;
 
-       rc = format_devname(buf, 1, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 1, sizeof(buf), "FOO");
        assert_int_equal(rc, -1);
 }
 
@@ -198,7 +211,7 @@ static void fd_mpath_short1(void **state)
        char buf[5];
        int rc;
 
-       rc = format_devname(buf, 27, sizeof(buf), "FOO");
+       rc = __format_devname(buf, 27, sizeof(buf), "FOO");
        assert_int_equal(rc, -1);
 }
 
@@ -323,7 +336,7 @@ static void sd_fd_many(void **state)
        int rc, i;
 
        for (i = 1; i < 5000; i++) {
-               rc = format_devname(buf, i, sizeof(buf), "MPATH");
+               rc = __format_devname(buf, i, sizeof(buf), "MPATH");
                assert_in_range(rc, 6, 8);
                rc = scan_devname(buf, "MPATH");
                assert_int_equal(rc, i);
@@ -338,7 +351,7 @@ static void sd_fd_random(void **state)
        srandom(1);
        for (i = 1; i < 1000; i++) {
                n = random() & 0xffff;
-               rc = format_devname(buf, n, sizeof(buf), "MPATH");
+               rc = __format_devname(buf, n, sizeof(buf), "MPATH");
                assert_in_range(rc, 6, 9);
                rc = scan_devname(buf, "MPATH");
                assert_int_equal(rc, n);
index 204cf1d977e09e40f7af1da438865efa8572e378..68c4ad46a13243ea7f6b7610503e6839ac5b2515 100644 (file)
@@ -323,12 +323,10 @@ int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
        return mock_type(int);
 }
 
-void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs,
-                               int purge_vec)
+void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs)
 {
        check_expected(alias);
        assert_ptr_equal(vecs, waiter->vecs);
-       assert_int_equal(purge_vec, 1);
 }
 
 /* pretend update the pretend dm devices. If fail is set, it
diff --git a/tests/strbuf.c b/tests/strbuf.c
new file mode 100644 (file)
index 0000000..43a477d
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2021 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <cmocka.h>
+#include <errno.h>
+#include "strbuf.h"
+#include "debug.h"
+#include "globals.c"
+
+void *__real_realloc(void *ptr, size_t size);
+
+static bool mock_realloc = false;
+void *__wrap_realloc(void *ptr, size_t size)
+{
+       void *p;
+       if (!mock_realloc)
+               return __real_realloc(ptr, size);
+
+       p = mock_ptr_type(void *);
+       condlog(4, "%s: %p, %zu -> %p", __func__, ptr, size, p);
+       return p;
+}
+
+static void test_strbuf_00(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       char *p;
+
+       assert_ptr_equal(buf.buf, NULL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+       p = steal_strbuf_str(&buf);
+       assert_ptr_equal(p, NULL);
+
+       assert_ptr_equal(buf.buf, NULL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       assert_int_equal(append_strbuf_str(&buf, "moin"), 4);
+       assert_int_equal(get_strbuf_len(&buf), 4);
+       assert_in_range(buf.size, 5, SIZE_MAX);
+       assert_string_equal(get_strbuf_str(&buf), "moin");
+       p = steal_strbuf_str(&buf);
+       assert_string_equal(p, "moin");
+       free(p);
+
+       assert_ptr_equal(buf.buf, NULL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       assert_int_equal(append_strbuf_str(&buf, NULL), -EINVAL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       assert_int_equal(append_strbuf_str(&buf, ""), 0);
+       /* appending a 0-length string allocates memory */
+       assert_in_range(buf.size, 1, SIZE_MAX);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+       p = steal_strbuf_str(&buf);
+       assert_string_equal(p, "");
+       free(p);
+
+       assert_int_equal(__append_strbuf_str(&buf, "x", 0), 0);
+       /* appending a 0-length string allocates memory */
+       assert_in_range(buf.size, 1, SIZE_MAX);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+}
+
+static void test_strbuf_alloc_err(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       size_t sz, ofs;
+       int rc;
+
+       mock_realloc = true;
+       will_return(__wrap_realloc, NULL);
+       assert_int_equal(append_strbuf_str(&buf, "moin"), -ENOMEM);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       mock_realloc = false;
+       assert_int_equal(append_strbuf_str(&buf, "moin"), 4);
+       sz = buf.size;
+       assert_in_range(sz, 5, SIZE_MAX);
+       assert_int_equal(buf.offs, 4);
+       assert_int_equal(get_strbuf_len(&buf), 4);
+       assert_string_equal(get_strbuf_str(&buf), "moin");
+
+       mock_realloc = true;
+       will_return(__wrap_realloc, NULL);
+       ofs = get_strbuf_len(&buf);
+       while ((rc = append_strbuf_str(&buf, " hello")) >= 0) {
+               condlog(3, "%s", get_strbuf_str(&buf));
+               assert_int_equal(rc, 6);
+               assert_int_equal(get_strbuf_len(&buf), ofs + 6);
+               assert_memory_equal(get_strbuf_str(&buf), "moin", 4);
+               assert_string_equal(get_strbuf_str(&buf) + ofs, " hello");
+               ofs = get_strbuf_len(&buf);
+       }
+       assert_int_equal(rc, -ENOMEM);
+       assert_int_equal(buf.size, sz);
+       assert_int_equal(get_strbuf_len(&buf), ofs);
+       assert_memory_equal(get_strbuf_str(&buf), "moin", 4);
+       assert_string_equal(get_strbuf_str(&buf) + ofs - 6, " hello");
+
+       reset_strbuf(&buf);
+       assert_ptr_equal(buf.buf, NULL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       mock_realloc = false;
+}
+
+static void test_strbuf_overflow(void **state)
+{
+       STRBUF_ON_STACK(buf);
+
+       assert_int_equal(append_strbuf_str(&buf, "x"), 1);
+       /* fake huge buffer */
+       buf.size = SIZE_MAX - 1;
+       buf.offs = buf.size - 1;
+       assert_int_equal(append_strbuf_str(&buf, "x"), -EOVERFLOW);
+}
+
+static void test_strbuf_big(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       const char big[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n";
+       char *bbig;
+       int i;
+
+       /* Under valgrind, 30000 iterations need ca. 30s on my laptop */
+       for (i = 0; i < 30000; i++) {
+               if (i % 1000 == 0)
+                       condlog(4, "%d", i);
+               assert_int_equal(append_strbuf_str(&buf, big), sizeof(big) - 1);
+               assert_int_equal(get_strbuf_len(&buf), (sizeof(big) - 1) * (i + 1));
+               assert_memory_equal(get_strbuf_str(&buf), big, sizeof(big) - 1);
+               assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf)
+                                   - (sizeof(big) - 1), big);
+       };
+       bbig = steal_strbuf_str(&buf);
+
+       assert_ptr_equal(buf.buf, NULL);
+       assert_int_equal(buf.size, 0);
+       assert_int_equal(buf.offs, 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       assert_int_equal(strlen(bbig), i * (sizeof(big) - 1));
+       assert_memory_equal(bbig, big, sizeof(big) - 1);
+       free(bbig);
+}
+
+static void test_strbuf_nul(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       char greet[] = "hello, sir!";
+
+       assert_int_equal(__append_strbuf_str(&buf, greet, 6), 6);
+       assert_string_equal(get_strbuf_str(&buf), "hello,");
+       assert_int_equal(__append_strbuf_str(&buf, greet, 6), 6);
+       assert_string_equal(get_strbuf_str(&buf), "hello,hello,");
+
+       /* overwrite comma with NUL; append_strbuf_str() stops at NUL byte */
+       greet[5] = '\0';
+       reset_strbuf(&buf);
+       assert_int_equal(append_strbuf_str(&buf, greet), 5);
+       assert_int_equal(get_strbuf_len(&buf), 5);
+       assert_string_equal(get_strbuf_str(&buf), "hello");
+       assert_int_equal(append_strbuf_str(&buf, greet), 5);
+       assert_int_equal(get_strbuf_len(&buf), 10);
+       assert_string_equal(get_strbuf_str(&buf), "hellohello");
+
+       /* __append_strbuf_str() appends full memory, including NUL bytes */
+       reset_strbuf(&buf);
+       assert_int_equal(__append_strbuf_str(&buf, greet, sizeof(greet) - 1),
+                        sizeof(greet) - 1);
+       assert_int_equal(get_strbuf_len(&buf), sizeof(greet) - 1);
+       assert_string_equal(get_strbuf_str(&buf), "hello");
+       assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!");
+       assert_int_equal(__append_strbuf_str(&buf, greet, sizeof(greet) - 1),
+                        sizeof(greet) - 1);
+       assert_string_equal(get_strbuf_str(&buf), "hello");
+       assert_int_equal(get_strbuf_len(&buf), 2 * (sizeof(greet) - 1));
+       assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!");
+}
+
+static void test_strbuf_quoted(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       const char said[] = "She said ";
+       const char greet[] = "hi, man!";
+       char *p;
+       size_t n;
+
+       assert_int_equal(append_strbuf_str(&buf, said), sizeof(said) - 1);
+       assert_int_equal(append_strbuf_quoted(&buf, greet), sizeof(greet) + 1);
+       assert_string_equal(get_strbuf_str(&buf), "She said \"hi, man!\"");
+       n = get_strbuf_len(&buf);
+       p = steal_strbuf_str(&buf);
+       assert_int_equal(append_strbuf_str(&buf, said), sizeof(said) - 1);
+       assert_int_equal(append_strbuf_quoted(&buf, p), n + 4);
+       assert_string_equal(get_strbuf_str(&buf),
+                           "She said \"She said \"\"hi, man!\"\"\"");
+       free(p);
+       n = get_strbuf_len(&buf);
+       p = steal_strbuf_str(&buf);
+       assert_int_equal(append_strbuf_str(&buf, said), sizeof(said) - 1);
+       assert_int_equal(append_strbuf_quoted(&buf, p), n + 8);
+       assert_string_equal(get_strbuf_str(&buf),
+                           "She said \"She said \"\"She said \"\"\"\"hi, man!\"\"\"\"\"\"\"");
+       free(p);
+}
+
+static void test_strbuf_escaped(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       const char said[] = "She said \"hi, man\"";
+
+       assert_int_equal(append_strbuf_quoted(&buf, said), sizeof(said) + 3);
+       assert_string_equal(get_strbuf_str(&buf),
+                           "\"She said \"\"hi, man\"\"\"");
+
+       reset_strbuf(&buf);
+       assert_int_equal(append_strbuf_quoted(&buf, "\""), 4);
+       assert_string_equal(get_strbuf_str(&buf), "\"\"\"\"");
+
+       reset_strbuf(&buf);
+       assert_int_equal(append_strbuf_quoted(&buf, "\"\""), 6);
+       assert_string_equal(get_strbuf_str(&buf), "\"\"\"\"\"\"");
+
+       reset_strbuf(&buf);
+       assert_int_equal(append_strbuf_quoted(&buf, "\"Hi\""), 8);
+       assert_string_equal(get_strbuf_str(&buf), "\"\"\"Hi\"\"\"");
+}
+
+#define SENTENCE "yields, preceded by itself, falsehood"
+static void test_print_strbuf(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       char sentence[] = SENTENCE;
+
+       assert_int_equal(print_strbuf(&buf, "\"%s\" %s.", sentence, sentence),
+                        2 * (sizeof(sentence) - 1) + 4);
+       assert_string_equal(get_strbuf_str(&buf),
+                           "\"" SENTENCE "\" " SENTENCE ".");
+       condlog(3, "%s", get_strbuf_str(&buf));
+
+       reset_strbuf(&buf);
+       assert_int_equal(print_strbuf(&buf, "0x%08x", 0xdeadbeef), 10);
+       assert_string_equal(get_strbuf_str(&buf), "0xdeadbeef");
+
+       reset_strbuf(&buf);
+       assert_int_equal(print_strbuf(&buf, "%d%% of %d is %0.2f",
+                                     5, 100, 0.05), 17);
+       assert_string_equal(get_strbuf_str(&buf), "5% of 100 is 0.05");
+}
+
+static void test_truncate_strbuf(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       const char str[] = "hello my dear!\n";
+       size_t sz, sz1;
+
+       assert_int_equal(truncate_strbuf(&buf, 1), -EFAULT);
+       assert_int_equal(truncate_strbuf(&buf, 0), -EFAULT);
+
+       assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1);
+       assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+       assert_string_equal(get_strbuf_str(&buf), str);
+
+       assert_int_equal(truncate_strbuf(&buf, sizeof(str)), -ERANGE);
+       assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+       assert_string_equal(get_strbuf_str(&buf), str);
+
+       assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 1), 0);
+       assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+       assert_string_equal(get_strbuf_str(&buf), str);
+
+       assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 2), 0);
+       assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 2);
+       assert_string_not_equal(get_strbuf_str(&buf), str);
+       assert_memory_equal(get_strbuf_str(&buf), str, sizeof(str) - 2);
+
+       assert_int_equal(truncate_strbuf(&buf, 5), 0);
+       assert_int_equal(get_strbuf_len(&buf), 5);
+       assert_string_not_equal(get_strbuf_str(&buf), str);
+       assert_string_equal(get_strbuf_str(&buf), "hello");
+
+       reset_strbuf(&buf);
+       assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1);
+
+       sz = buf.size;
+       while (buf.size == sz)
+               assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1);
+
+       sz1  = buf.size;
+       assert_in_range(get_strbuf_len(&buf), sz + 1, SIZE_MAX);
+       assert_string_equal(get_strbuf_str(&buf) +
+                           get_strbuf_len(&buf) - (sizeof(str) - 1), str);
+       assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf) + 1),
+                        -ERANGE);
+       assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf)), 0);
+       assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf)
+                                        - (sizeof(str) - 1)), 0);
+       assert_in_range(get_strbuf_len(&buf), 1, sz);
+       assert_string_equal(get_strbuf_str(&buf) +
+                           get_strbuf_len(&buf) - (sizeof(str) - 1), str);
+       assert_int_equal(buf.size, sz1);
+
+       assert_int_equal(truncate_strbuf(&buf, 5), 0);
+       assert_int_equal(get_strbuf_len(&buf), 5);
+       assert_string_equal(get_strbuf_str(&buf), "hello");
+       assert_int_equal(buf.size, sz1);
+
+       assert_int_equal(truncate_strbuf(&buf, 0), 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+       assert_int_equal(buf.size, sz1);
+}
+
+static void test_fill_strbuf(void **state)
+{
+       STRBUF_ON_STACK(buf);
+       int i;
+       char *p;
+
+       assert_int_equal(fill_strbuf(&buf, '+', -5), -EINVAL);
+
+       assert_int_equal(fill_strbuf(&buf, '+', 0), 0);
+       assert_int_equal(get_strbuf_len(&buf), 0);
+       assert_string_equal(get_strbuf_str(&buf), "");
+
+       assert_int_equal(fill_strbuf(&buf, '+', 1), 1);
+       assert_int_equal(get_strbuf_len(&buf), 1);
+       assert_string_equal(get_strbuf_str(&buf), "+");
+
+       assert_int_equal(fill_strbuf(&buf, '-', 3), 3);
+       assert_int_equal(get_strbuf_len(&buf), 4);
+       assert_string_equal(get_strbuf_str(&buf), "+---");
+
+       assert_int_equal(fill_strbuf(&buf, '\0', 3), 3);
+       assert_int_equal(get_strbuf_len(&buf), 7);
+       assert_string_equal(get_strbuf_str(&buf), "+---");
+
+       truncate_strbuf(&buf, 4);
+       assert_int_equal(fill_strbuf(&buf, '+', 4), 4);
+       assert_int_equal(get_strbuf_len(&buf), 8);
+       assert_string_equal(get_strbuf_str(&buf), "+---++++");
+
+       reset_strbuf(&buf);
+       assert_int_equal(fill_strbuf(&buf, 'x', 30000), 30000);
+       assert_int_equal(get_strbuf_len(&buf), 30000);
+       p = steal_strbuf_str(&buf);
+       assert_int_equal(strlen(p), 30000);
+       for (i = 0; i < 30000; i++)
+               assert_int_equal(p[i], 'x');
+       free(p);
+}
+
+static int test_strbuf(void)
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test(test_strbuf_00),
+               cmocka_unit_test(test_strbuf_alloc_err),
+               cmocka_unit_test(test_strbuf_overflow),
+               cmocka_unit_test(test_strbuf_big),
+               cmocka_unit_test(test_strbuf_nul),
+               cmocka_unit_test(test_strbuf_quoted),
+               cmocka_unit_test(test_strbuf_escaped),
+               cmocka_unit_test(test_print_strbuf),
+               cmocka_unit_test(test_truncate_strbuf),
+               cmocka_unit_test(test_fill_strbuf),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+       int ret = 0;
+
+       init_test_verbosity(-1);
+       ret += test_strbuf();
+       return ret;
+}