On Wed, Aug 11, 2021 at 05:41:46PM +0200, [email protected] wrote:
> From: Martin Wilck <[email protected]>
> 
> Use the strbuf API in print.c, wherever growing string buffers are
> appropriate. This requires a large amount of changes in print.c itself,
> and in other files that use functions from print.c. It makes no sense
> to separate this into smaller patches though, as the various snprint_xyz()
> functions depend on each other, and need to use similar prototypes.
> 
> libmultipath version must be bumped, as all the printing functions have
> changed prototypes. Also, add the required strbuf functions to the
> libmultipath API.
> 
> Change the libmultipath calls in prioritizers, foreign libs,
> and in multipathd according to the strbuf-related API changes.
> 
> While working on print.c, I made a couple of other other minor changes:
>  - all snprint_xyz() functions return a negative error in case of failure
>    (most likely is -ENOMEM). In case of success, the number of printed
>    characters is returned.
>  - snprint_progress(): use fill_strbuf() rather than repeated iterations
>  - _snprint_multipath(), _snprint_path(), snprint_multipath_header(),
>    snprint_path_header(), _snprint_pathgroup(): use fill_strbuf()
>    instead of the PAD/NOPAD logic.
>  - _snprint_multipath_topology(): simplified the ASCII art generation for
>    the topology tree somewhat.
>  - snprint_json_xyz(): use fill_strbuf() rather than printing the indent
>    repeatedly.
>  - snprint_blacklist_group(), snprint_blacklist_devgroup(): combined two
>    print statements into one.
> 
> In cli_handlers.c, fixed the returned values for the "len" parameter in
> some functions (it's supposed to be the strlen of the reply + 1).
> 
> Signed-off-by: Martin Wilck <[email protected]>

Reviewed-by: Benjamin Marzinski <[email protected]>

> ---
>  libmultipath/blacklist.c                 |   13 +-
>  libmultipath/discovery.c                 |   13 +-
>  libmultipath/foreign.c                   |   78 +-
>  libmultipath/foreign.h                   |   13 +-
>  libmultipath/foreign/nvme.c              |  100 +-
>  libmultipath/generic.c                   |   26 +-
>  libmultipath/generic.h                   |   17 +-
>  libmultipath/libmultipath.version        |   14 +-
>  libmultipath/parser.c                    |    5 +-
>  libmultipath/parser.h                    |    2 +-
>  libmultipath/print.c                     | 1696 +++++++++-------------
>  libmultipath/print.h                     |   67 +-
>  libmultipath/prioritizers/weightedpath.c |   71 +-
>  multipathd/cli_handlers.c                |  345 ++---
>  14 files changed, 1000 insertions(+), 1460 deletions(-)
> 
> diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
> index 6c6a597..4e315c9 100644
> --- a/libmultipath/blacklist.c
> +++ b/libmultipath/blacklist.c
> @@ -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;
>  }
>  
> diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
> index e9f5703..f25fe9e 100644
> --- a/libmultipath/discovery.c
> +++ b/libmultipath/discovery.c
> @@ -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" },
> @@ -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;
>  }
> @@ -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;
> diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c
> index fce1934..e091a1d 100644
> --- a/libmultipath/foreign.c
> +++ b/libmultipath/foreign.c
> @@ -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;
>  }
> diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h
> index acd3360..77fc485 100644
> --- a/libmultipath/foreign.h
> +++ b/libmultipath/foreign.h
> @@ -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);
>  
>  /**
> diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
> index b726be2..d40c086 100644
> --- a/libmultipath/foreign/nvme.c
> +++ b/libmultipath/foreign/nvme.c
> @@ -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 = {
> diff --git a/libmultipath/generic.c b/libmultipath/generic.c
> index 5f03b9e..e7cf297 100644
> --- a/libmultipath/generic.c
> +++ b/libmultipath/generic.c
> @@ -15,23 +15,23 @@
>    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) " : "");
>  }
> diff --git a/libmultipath/generic.h b/libmultipath/generic.h
> index 6346ffe..57c123c 100644
> --- a/libmultipath/generic.h
> +++ b/libmultipath/generic.h
> @@ -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 */
> diff --git a/libmultipath/libmultipath.version 
> b/libmultipath/libmultipath.version
> index 6dd52c2..1d84d97 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -31,7 +31,7 @@
>   *   The new version inherits the previous ones.
>   */
>  
> -LIBMULTIPATH_7.0.0 {
> +LIBMULTIPATH_8.0.0 {
>  global:
>       /* symbols referenced by multipath and multipathd */
>       add_foreign;
> @@ -273,3 +273,15 @@ global:
>  local:
>       *;
>  };
> +
> +LIBMULTIPATH_8.1.0 {
> +global:
> +     reset_strbuf;
> +     append_strbuf_str;
> +     get_strbuf_len;
> +     get_strbuf_str;
> +     steal_strbuf_str;
> +     fill_strbuf;
> +     print_strbuf;
> +     truncate_strbuf;
> +} LIBMULTIPATH_8.0.0;
> diff --git a/libmultipath/parser.c b/libmultipath/parser.c
> index 88c7b59..8ca91bf 100644
> --- a/libmultipath/parser.c
> +++ b/libmultipath/parser.c
> @@ -150,7 +150,7 @@ 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;
> @@ -191,7 +191,8 @@ snprint_keyword(char *buff, int len, char *fmt, struct 
> keyword *kw,
>               }
>       } while (*fmt++);
>  out:
> -     return snprintf(buff, len, "%s", get_strbuf_str(&sbuf));
> +     return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
> +                                get_strbuf_len(&sbuf));
>  }
>  
>  static const char quote_marker[] = { '\0', '"', '\0' };
> diff --git a/libmultipath/parser.h b/libmultipath/parser.h
> index e8b6eb2..b43d46f 100644
> --- a/libmultipath/parser.h
> +++ b/libmultipath/parser.h
> @@ -82,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);
>  
> diff --git a/libmultipath/print.c b/libmultipath/print.c
> index 29ce499..dd04dea 100644
> --- a/libmultipath/print.c
> +++ b/libmultipath/print.c
> @@ -1,4 +1,4 @@
> - /*
> +/*
>   * Copyright (c) 2005 Christophe Varoqui
>   */
>  #include <stdio.h>
> @@ -31,59 +31,33 @@
>  #include "discovery.h"
>  #include "util.h"
>  #include "foreign.h"
> +#include "strbuf.h"
>  
>  #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 +68,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 +247,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 +265,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 +281,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 +297,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 +340,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:%" PRIu64,
> +     return print_strbuf(buff, "%i:%i:%i:%" PRIu64,
>                       pp->sg_id.host_no,
>                       pp->sg_id.channel,
>                       pp->sg_id.scsi_id,
> @@ -400,159 +367,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 +526,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,28 +536,28 @@ 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[42];
> @@ -599,7 +565,7 @@ snprint_tgt_wwpn (char * buff, size_t len, const struct 
> path * pp)
>       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 +577,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 +748,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 +807,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 +818,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 +848,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 +859,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 +880,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 +903,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 +926,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;
> +     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 = 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] = {};
> +     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 = 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;
> -
> -     do {
> -             if (TAIL <= 0)
> -                     break;
> +     int initial_len = get_strbuf_len(line);
> +     const char *f;
> +     struct path_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 = 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,268 +1095,203 @@ 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");
>  
> @@ -1445,57 +1303,53 @@ snprint_hwentry (const struct config *conf,
>       if (!rootkw)
>               return 0;
>  
> -     fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
> -     if (fwd >= len)
> -             return len;
> +     if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
> +             return rc;
> +
>       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;
>  
> -     fwd += snprintf(buff + fwd, len - fwd, "devices {\n");
> -     if (fwd >= len)
> -             return len;
> +     if ((rc = append_strbuf_str(buff, "devices {\n")) < 0)
> +             return rc;
> +
>       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;
> @@ -1504,49 +1358,44 @@ snprint_mpentry (const struct config *conf, char * 
> buff, int len,
>       if (!rootkw)
>               return 0;
>  
> -     fwd += snprintf(buff + fwd, len - fwd, "\tmultipath {\n");
> -     if (fwd >= len)
> -             return len;
> +     if ((rc = append_strbuf_str(buff, "\tmultipath {\n")) < 0)
> +             return rc;
> +
>       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;
>  
> -     fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n");
> -     if (fwd >= len)
> -             return len;
> +     if ((rc = append_strbuf_str(buff, "multipaths {\n")) < 0)
> +             return rc;
> +
>       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 +1404,397 @@ 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;
>  
> -     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;
>  
> -     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;
> +     size_t initial_len = get_strbuf_len(buff);
>  
>       rootkw = find_keyword(conf->keywords, NULL, "blacklist");
>       if (!rootkw)
>               return 0;
>  
> -     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;
> +             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;
> +             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;
> +             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;
> +             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;
>  
>       vector_foreach_slot (conf->blist_device, bled, i) {
> -             fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
> -             if (fwd >= len)
> -                     return len;
> +             if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
> +                     return rc;
> +
>               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;
> +             if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, bled)) < 0)
> +                     return rc;
>               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,
> +                                       "\t\t%k %v\n\t}\n", kw, 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;
> +     size_t initial_len = get_strbuf_len(buff);
>  
>       rootkw = find_keyword(conf->keywords, NULL, "blacklist_exceptions");
>       if (!rootkw)
>               return 0;
>  
> -     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;
> +             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;
> +             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;
> +             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;
> +             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;
>  
>       vector_foreach_slot (conf->elist_device, eled, i) {
> -             fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
> -             if (fwd >= len)
> -                     return len;
> +             if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
> +                     return rc;
> +
>               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;
> +             if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, eled)) < 0)
> +                     return rc;
>               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, "\t\t%k %v\n\t}\n",
> +                                       kw, 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;
> +     if (len)
> +             *len = get_strbuf_len(&buff);
> +     reply = steal_strbuf_str(&buff);
>  
> -             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 (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 +1816,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 +1838,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 +1867,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);
>  }
> diff --git a/libmultipath/print.h b/libmultipath/print.h
> index 0042cef..b922812 100644
> --- a/libmultipath/print.h
> +++ b/libmultipath/print.h
> @@ -2,6 +2,8 @@
>  #define _PRINT_H
>  #include "dm-generic.h"
>  
> +struct strbuf;
> +
>  #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"
> @@ -25,7 +27,7 @@
>  #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_INDENT_N    3
>  #define PRINT_JSON_MAP       "{\n" \
>                            "      \"name\" : \"%n\",\n" \
>                            "      \"uuid\" : \"%w\",\n" \
> @@ -79,21 +81,21 @@ 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 +108,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 +144,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 */
> diff --git a/libmultipath/prioritizers/weightedpath.c 
> b/libmultipath/prioritizers/weightedpath.c
> index 650088b..ea03fc3 100644
> --- a/libmultipath/prioritizers/weightedpath.c
> +++ b/libmultipath/prioritizers/weightedpath.c
> @@ -35,52 +35,37 @@
>  #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:%" PRIu64, 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;
>  }
>  
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index bce40b1..6d3a0ae 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -30,6 +30,7 @@
>  #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)                      
> \
> @@ -42,49 +43,30 @@ 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;
>  }
>  
> @@ -92,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;
>  }
>  
> @@ -121,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)) {
> @@ -206,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;
>  }
>  
> @@ -228,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;
>  }
>  
> @@ -420,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;
>  }
>  
> @@ -479,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;
>  }
>  
> @@ -506,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;
>  }
>  
> @@ -1366,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;
>  }
>  
> @@ -1408,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;
>  }
> -- 
> 2.32.0

--
dm-devel mailing list
[email protected]
https://listman.redhat.com/mailman/listinfo/dm-devel

Reply via email to