]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: rework printfrr extensions to output directly
authorDavid Lamparter <equinox@diac24.net>
Thu, 18 Feb 2021 21:52:23 +0000 (22:52 +0100)
committerDavid Lamparter <equinox@diac24.net>
Sat, 27 Mar 2021 15:56:55 +0000 (16:56 +0100)
Allowing printfrr extensions to directly write to the output buffer has
a few advantages:
- there is no arbitrary length limit imposed (previously 64)
- the output doesn't need to be copied another time
- the extension can directly use bprintfrr() to put together pieces

The downside is that the theoretical length (regardless of available
buffer space) must be computed correctly.

Extended unit tests to test these paths a bit more thoroughly.

Signed-off-by: David Lamparter <equinox@diac24.net>
bgpd/bgp_table.c
lib/nexthop.c
lib/prefix.c
lib/printf/glue.c
lib/printf/printflocal.h
lib/printf/vfprintf.c
lib/printfrr.h
lib/sockunion.c
lib/srcdest_table.c
tests/lib/test_printfrr.c

index 7e3aa2a48a50bc4c9fa3861f5e517a32868370e3..50123c608b777c52f1f1084f7fc5535f907363df 100644 (file)
@@ -201,18 +201,17 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table,
 }
 
 printfrr_ext_autoreg_p("BD", printfrr_bd)
-static ssize_t printfrr_bd(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_bd(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
        const struct bgp_dest *dest = ptr;
-       const struct prefix *p;
+       const struct prefix *p = bgp_dest_get_prefix(dest);
+       char cbuf[PREFIX_STRLEN];
 
-       if (dest) {
-               p = bgp_dest_get_prefix(dest);
-               prefix2str(p, buf, bsz);
-       } else {
-               strlcpy(buf, "NULL", bsz);
-       }
+       if (!dest)
+               return bputs(buf, "NULL");
 
-       return 2;
+       /* need to get the real length even if buffer too small */
+       prefix2str(p, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
index 17ef95c68727290ee9885855cd9814d08220b34d..64b4f3adb110bd2a6cb9f7c0e1ad476c85b2bf19 100644 (file)
@@ -730,80 +730,99 @@ int nexthop_str2backups(const char *str, int *num_backups,
  *             nexthop2str()
  */
 printfrr_ext_autoreg_p("NH", printfrr_nh)
-static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_nh(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
        const struct nexthop *nexthop = ptr;
-       struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
        bool do_ifi = false;
-       const char *s, *v_is = "", *v_via = "", *v_viaif = "via ";
-       ssize_t ret = 3;
+       const char *v_is = "", *v_via = "", *v_viaif = "via ";
+       ssize_t ret = 0;
 
-       /* NULL-check */
-       if (nexthop == NULL) {
-               if (fmt[2] == 'v' && fmt[3] == 'v')
-                       ret++;
-
-               strlcpy(buf, "NULL", bsz);
-
-               return ret;
-       }
-
-       switch (fmt[2]) {
+       switch (**fmt) {
        case 'v':
-               if (fmt[3] == 'v') {
+               (*fmt)++;
+               if (**fmt == 'v') {
                        v_is = "is ";
                        v_via = "via ";
                        v_viaif = "";
-                       ret++;
+                       (*fmt)++;
                }
 
+               if (!nexthop)
+                       return bputs(buf, "NULL");
+
                switch (nexthop->type) {
                case NEXTHOP_TYPE_IPV4:
                case NEXTHOP_TYPE_IPV4_IFINDEX:
-                       bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4);
+                       ret += bprintfrr(buf, "%s%pI4", v_via,
+                                        &nexthop->gate.ipv4);
                        do_ifi = true;
                        break;
                case NEXTHOP_TYPE_IPV6:
                case NEXTHOP_TYPE_IPV6_IFINDEX:
-                       bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6);
+                       ret += bprintfrr(buf, "%s%pI6", v_via,
+                                        &nexthop->gate.ipv6);
                        do_ifi = true;
                        break;
                case NEXTHOP_TYPE_IFINDEX:
-                       bprintfrr(&fb, "%sdirectly connected, %s", v_is,
-                               ifindex2ifname(nexthop->ifindex,
-                                              nexthop->vrf_id));
+                       ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
+                                        ifindex2ifname(nexthop->ifindex,
+                                                       nexthop->vrf_id));
                        break;
                case NEXTHOP_TYPE_BLACKHOLE:
+                       ret += bputs(buf, "unreachable");
+
                        switch (nexthop->bh_type) {
                        case BLACKHOLE_REJECT:
-                               s = " (ICMP unreachable)";
+                               ret += bputs(buf, " (ICMP unreachable)");
                                break;
                        case BLACKHOLE_ADMINPROHIB:
-                               s = " (ICMP admin-prohibited)";
+                               ret += bputs(buf, " (ICMP admin-prohibited)");
                                break;
                        case BLACKHOLE_NULL:
-                               s = " (blackhole)";
+                               ret += bputs(buf, " (blackhole)");
                                break;
                        default:
-                               s = "";
                                break;
                        }
-                       bprintfrr(&fb, "unreachable%s", s);
                        break;
                default:
                        break;
                }
                if (do_ifi && nexthop->ifindex)
-                       bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname(
-                                       nexthop->ifindex,
-                                       nexthop->vrf_id));
+                       ret += bprintfrr(buf, ", %s%s", v_viaif,
+                                        ifindex2ifname(nexthop->ifindex,
+                                                       nexthop->vrf_id));
 
-               *fb.pos = '\0';
                return ret;
        case 's':
-               nexthop2str(nexthop, buf, bsz);
-               return 3;
+               (*fmt)++;
+
+               if (!nexthop)
+                       return bputs(buf, "NULL");
+
+               switch (nexthop->type) {
+               case NEXTHOP_TYPE_IFINDEX:
+                       ret += bprintfrr(buf, "if %u", nexthop->ifindex);
+                       break;
+               case NEXTHOP_TYPE_IPV4:
+               case NEXTHOP_TYPE_IPV4_IFINDEX:
+                       ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4,
+                                        nexthop->ifindex);
+                       break;
+               case NEXTHOP_TYPE_IPV6:
+               case NEXTHOP_TYPE_IPV6_IFINDEX:
+                       ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6,
+                                        nexthop->ifindex);
+                       break;
+               case NEXTHOP_TYPE_BLACKHOLE:
+                       ret += bputs(buf, "blackhole");
+                       break;
+               default:
+                       ret += bputs(buf, "unknown");
+                       break;
+               }
+               return ret;
        }
-       return 0;
+       return -1;
 }
index afc4d3d5c22f5bfaa05b446e0eb952a79b61d19d..169e9ed57276f07b14994eda949672328c6cc33f 100644 (file)
@@ -1361,92 +1361,92 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
 }
 
 printfrr_ext_autoreg_p("EA", printfrr_ea)
-static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_ea(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
        const struct ethaddr *mac = ptr;
+       char cbuf[ETHER_ADDR_STRLEN];
 
-       if (mac)
-               prefix_mac2str(mac, buf, bsz);
-       else
-               strlcpy(buf, "NULL", bsz);
+       if (!mac)
+               return bputs(buf, "NULL");
 
-       return 2;
+       /* need real length even if buffer is too short */
+       prefix_mac2str(mac, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 printfrr_ext_autoreg_p("IA", printfrr_ia)
-static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_ia(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
        const struct ipaddr *ipa = ptr;
+       char cbuf[INET6_ADDRSTRLEN];
 
-       if (ipa)
-               ipaddr2str(ipa, buf, bsz);
-       else
-               strlcpy(buf, "NULL", bsz);
+       if (!ipa)
+               return bputs(buf, "NULL");
 
-       return 2;
+       ipaddr2str(ipa, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 printfrr_ext_autoreg_p("I4", printfrr_i4)
-static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_i4(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
-       if (ptr)
-               inet_ntop(AF_INET, ptr, buf, bsz);
-       else
-               strlcpy(buf, "NULL", bsz);
+       char cbuf[INET_ADDRSTRLEN];
+
+       if (!ptr)
+               return bputs(buf, "NULL");
 
-       return 2;
+       inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 printfrr_ext_autoreg_p("I6", printfrr_i6)
-static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_i6(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
-       if (ptr)
-               inet_ntop(AF_INET6, ptr, buf, bsz);
-       else
-               strlcpy(buf, "NULL", bsz);
+       char cbuf[INET6_ADDRSTRLEN];
+
+       if (!ptr)
+               return bputs(buf, "NULL");
 
-       return 2;
+       inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 printfrr_ext_autoreg_p("FX", printfrr_pfx)
-static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_pfx(struct fbuf *buf, const char **fmt,
                            int prec, const void *ptr)
 {
-       if (ptr)
-               prefix2str(ptr, buf, bsz);
-       else
-               strlcpy(buf, "NULL", bsz);
+       char cbuf[PREFIX_STRLEN];
+
+       if (!ptr)
+               return bputs(buf, "NULL");
 
-       return 2;
+       prefix2str(ptr, cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 printfrr_ext_autoreg_p("SG4", printfrr_psg)
-static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_psg(struct fbuf *buf, const char **fmt,
                            int prec, const void *ptr)
 {
        const struct prefix_sg *sg = ptr;
-       struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
+       ssize_t ret = 0;
 
-       if (sg) {
-               if (sg->src.s_addr == INADDR_ANY)
-                       bprintfrr(&fb, "(*,");
-               else
-                       bprintfrr(&fb, "(%pI4,", &sg->src);
-
-               if (sg->grp.s_addr == INADDR_ANY)
-                       bprintfrr(&fb, "*)");
-               else
-                       bprintfrr(&fb, "%pI4)", &sg->grp);
+       if (!sg)
+               return bputs(buf, "NULL");
 
-               fb.pos[0] = '\0';
+       if (sg->src.s_addr == INADDR_ANY)
+               ret += bputs(buf, "(*,");
+       else
+               ret += bprintfrr(buf, "(%pI4,", &sg->src);
 
-       } else {
-               strlcpy(buf, "NULL", bsz);
-       }
+       if (sg->grp.s_addr == INADDR_ANY)
+               ret += bputs(buf, "*)");
+       else
+               ret += bprintfrr(buf, "%pI4)", &sg->grp);
 
-       return 3;
+       return ret;
 }
index 29ca26ad5da184d7e9653e4c7704c7ecca82e109..477fc0d384459d003dab9f15200e2af8ba07f8d0 100644 (file)
@@ -210,15 +210,16 @@ void printfrr_ext_reg(const struct printfrr_ext *ext)
        exts[i] = ext;
 }
 
-ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
+ssize_t printfrr_extp(struct fbuf *buf, const char **fmtp, int prec,
                      const void *ptr)
 {
+       const char *fmt = *fmtp;
        const struct printfrr_ext *ext;
        size_t i;
 
        for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
                if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
-                       return 0;
+                       return -1;
                if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
                        continue;
                ext = exts[i];
@@ -226,20 +227,22 @@ ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec,
                        continue;
                if (strncmp(ext->match, fmt, strlen(ext->match)))
                        continue;
-               return ext->print_ptr(buf, sz, fmt, prec, ptr);
+               *fmtp += strlen(ext->match);
+               return ext->print_ptr(buf, fmtp, prec, ptr);
        }
-       return 0;
+       return -1;
 }
 
-ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
+ssize_t printfrr_exti(struct fbuf *buf, const char **fmtp, int prec,
                      uintmax_t num)
 {
+       const char *fmt = *fmtp;
        const struct printfrr_ext *ext;
        size_t i;
 
        for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
                if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
-                       return 0;
+                       return -1;
                if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
                        continue;
                ext = exts[i];
@@ -247,7 +250,8 @@ ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec,
                        continue;
                if (strncmp(ext->match, fmt, strlen(ext->match)))
                        continue;
-               return ext->print_int(buf, sz, fmt, prec, num);
+               *fmtp += strlen(ext->match);
+               return ext->print_int(buf, fmtp, prec, num);
        }
-       return 0;
+       return -1;
 }
index 335e09872e29470fedb21c82d85aac9324a54986..df962fc043ee48f01fad9314e2d731d7ff9f3eeb 100644 (file)
@@ -100,6 +100,7 @@ int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL;
 int    _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL;
 #endif
 
-/* returns number of bytes consumed for extended specifier */
-ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL;
-ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL;
+/* returns number of bytes needed for full output, or -1 */
+ssize_t printfrr_extp(struct fbuf *, const char **, int, const void *)
+       DSO_LOCAL;
+ssize_t printfrr_exti(struct fbuf *, const char **, int, uintmax_t) DSO_LOCAL;
index a0634cde4b9bc07fce193a117ccba7c95bbe6a32..3b5bda965a1baf8bafda92c9268feafb034f65a2 100644 (file)
@@ -177,6 +177,7 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap)
        int nextarg;            /* 1-based argument index */
        va_list orgap;          /* original argument pointer */
        char *convbuf;          /* wide to multibyte conversion result */
+       char *extstart = NULL;  /* where printfrr_ext* started printing */
 
        static const char xdigs_lower[16] = "0123456789abcdef";
        static const char xdigs_upper[16] = "0123456789ABCDEF";
@@ -438,16 +439,14 @@ reswitch: switch (ch) {
                                ulval = SARG();
 
                        if (printfrr_ext_char(fmt[0])) {
-                               n2 = printfrr_exti(buf, sizeof(buf), fmt, prec,
+                               if (cb)
+                                       extstart = cb->pos;
+
+                               size = printfrr_exti(cb, &fmt, prec,
                                                (flags & INTMAX_SIZE) ? ujval
                                                : (uintmax_t)ulval);
-                               if (n2 > 0) {
-                                       fmt += n2;
-                                       cp = buf;
-                                       size = strlen(cp);
-                                       sign = '\0';
-                                       break;
-                               }
+                               if (size >= 0)
+                                       goto ext_printed;
                        }
                        if (flags & INTMAX_SIZE) {
                                if ((intmax_t)ujval < 0) {
@@ -551,14 +550,13 @@ reswitch: switch (ch) {
                         *      -- ANSI X3J11
                         */
                        ptrval = GETARG(void *);
-                       if (printfrr_ext_char(fmt[0]) &&
-                                       (n2 = printfrr_extp(buf, sizeof(buf),
-                                               fmt, prec, ptrval)) > 0) {
-                               fmt += n2;
-                               cp = buf;
-                               size = strlen(cp);
-                               sign = '\0';
-                               break;
+                       if (printfrr_ext_char(fmt[0])) {
+                               if (cb)
+                                       extstart = cb->pos;
+
+                               size = printfrr_extp(cb, &fmt, prec, ptrval);
+                               if (size >= 0)
+                                       goto ext_printed;
                        }
                        ujval = (uintmax_t)(uintptr_t)ptrval;
                        base = 16;
@@ -686,7 +684,7 @@ number:                     if ((dprec = prec) >= 0)
                        realsz += 2;
 
                prsize = width > realsz ? width : realsz;
-               if ((unsigned)ret + prsize > INT_MAX) {
+               if ((unsigned int)ret + prsize > INT_MAX) {
                        ret = EOF;
                        errno = EOVERFLOW;
                        goto error;
@@ -720,6 +718,58 @@ number:                    if ((dprec = prec) >= 0)
                /* finally, adjust ret */
                ret += prsize;
 
+               FLUSH();        /* copy out the I/O vectors */
+               continue;
+
+ext_printed:
+               /* when we arrive here, a printfrr extension has written to cb
+                * (if non-NULL), but we still need to handle padding.  The
+                * original cb->pos is in extstart;  the return value from the
+                * ext is in size.
+                *
+                * Keep analogous to code above please.
+                */
+
+               realsz = size;
+               prsize = width > realsz ? width : realsz;
+               if ((unsigned int)ret + prsize > INT_MAX) {
+                       ret = EOF;
+                       errno = EOVERFLOW;
+                       goto error;
+               }
+
+               /* right-adjusting blank padding - need to move the chars
+                * that the extension has already written.  Should be very
+                * rare.
+                */
+               if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) {
+                       size_t nwritten = cb->pos - extstart;
+                       size_t navail = cb->buf + cb->len - extstart;
+                       size_t npad = width - realsz;
+                       size_t nmove;
+
+                       if (navail < npad)
+                               navail = 0;
+                       else
+                               navail -= npad;
+                       nmove = MIN(nwritten, navail);
+
+                       memmove(extstart + npad, extstart, nmove);
+
+                       cb->pos = extstart;
+                       PAD(npad, blanks);
+                       cb->pos += nmove;
+               }
+
+               io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0;
+
+               /* left-adjusting padding (always blank) */
+               if (flags & LADJUST)
+                       PAD(width - realsz, blanks);
+
+               /* finally, adjust ret */
+               ret += prsize;
+
                FLUSH();        /* copy out the I/O vectors */
        }
 done:
index 418e839d97ce5bbe9c6119d5abc13f32c722c02a..549334ba5ba444e23ed51f453885e7a4be1d1527 100644 (file)
@@ -112,20 +112,21 @@ struct printfrr_ext {
        /* both can be given, if not the code continues searching
         * (you can do %pX and %dX in 2 different entries)
         *
-        * return value: number of bytes consumed from the format string, so
-        * you can consume extra flags (e.g. register for "%pX", consume
-        * "%pXfoo" or "%pXbar" for flags.)  Convention is to make those flags
-        * lowercase letters or numbers.
+        * return value: number of bytes that would be printed if the buffer
+        * was large enough.  be careful about not under-reporting this;
+        * otherwise asnprintf() & co. will get broken.  Returning -1 means
+        * something went wrong & default %p/%d handling should be executed.
         *
-        * bsz is a compile-time constant in printf;  it's gonna be relatively
-        * small.  This isn't designed to print Shakespeare from a pointer.
+        * to consume extra input flags after %pXY, increment *fmt.  It points
+        * at the first character after %pXY at entry.  Convention is to make
+        * those flags lowercase letters or numbers.
         *
         * prec is the precision specifier (the 999 in "%.999p")  -1 means
         * none given (value in the format string cannot be negative)
         */
-       ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec,
+       ssize_t (*print_ptr)(struct fbuf *buf, const char **fmt, int prec,
                        const void *);
-       ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec,
+       ssize_t (*print_int)(struct fbuf *buf, const char **fmt, int prec,
                        uintmax_t);
 };
 
@@ -136,7 +137,7 @@ struct printfrr_ext {
 void printfrr_ext_reg(const struct printfrr_ext *);
 
 #define printfrr_ext_autoreg_p(matchs, print_fn)                               \
-       static ssize_t print_fn(char *, size_t, const char *, int,             \
+       static ssize_t print_fn(struct fbuf *, const char **, int,             \
                                const void *);                                 \
        static const struct printfrr_ext _printext_##print_fn = {              \
                .match = matchs,                                               \
@@ -149,7 +150,7 @@ void printfrr_ext_reg(const struct printfrr_ext *);
        /* end */
 
 #define printfrr_ext_autoreg_i(matchs, print_fn)                               \
-       static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \
+       static ssize_t print_fn(struct fbuf *, const char **, int, uintmax_t); \
        static const struct printfrr_ext _printext_##print_fn = {              \
                .match = matchs,                                               \
                .print_int = print_fn,                                         \
index d65235b41cb3435fdd6abf526be05b5ed93cc3f8..25de8f7dd0040f65cbe68098d7b124b3f5274248 100644 (file)
@@ -664,54 +664,51 @@ void sockunion_init(union sockunion *su)
 }
 
 printfrr_ext_autoreg_p("SU", printfrr_psu)
-static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_psu(struct fbuf *buf, const char **fmt,
                            int prec, const void *ptr)
 {
        const union sockunion *su = ptr;
-       struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 };
        bool include_port = false;
        bool endflags = false;
-       ssize_t consumed = 2;
-
-       if (su) {
-               while (!endflags) {
-                       switch (fmt[consumed++]) {
-                       case 'p':
-                               include_port = true;
-                               break;
-                       default:
-                               consumed--;
-                               endflags = true;
-                               break;
-                       }
-               };
-
-               switch (sockunion_family(su)) {
-               case AF_UNSPEC:
-                       bprintfrr(&fb, "(unspec)");
-                       break;
-               case AF_INET:
-                       inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz);
-                       fb.pos += strlen(fb.buf);
-                       if (include_port)
-                               bprintfrr(&fb, ":%d", su->sin.sin_port);
-                       break;
-               case AF_INET6:
-                       inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz);
-                       fb.pos += strlen(fb.buf);
-                       if (include_port)
-                               bprintfrr(&fb, ":%d", su->sin6.sin6_port);
+       ssize_t ret = 0;
+       char cbuf[INET6_ADDRSTRLEN];
+
+       if (!su)
+               return bputs(buf, "NULL");
+
+       while (!endflags) {
+               switch (**fmt) {
+               case 'p':
+                       (*fmt)++;
+                       include_port = true;
                        break;
                default:
-                       bprintfrr(&fb, "(af %d)", sockunion_family(su));
+                       endflags = true;
+                       break;
                }
+       }
 
-               fb.pos[0] = '\0';
-       } else {
-               strlcpy(buf, "NULL", bsz);
+       switch (sockunion_family(su)) {
+       case AF_UNSPEC:
+               ret += bputs(buf, "(unspec)");
+               break;
+       case AF_INET:
+               inet_ntop(AF_INET, &su->sin.sin_addr, cbuf, sizeof(cbuf));
+               ret += bputs(buf, cbuf);
+               if (include_port)
+                       ret += bprintfrr(buf, ":%d", su->sin.sin_port);
+               break;
+       case AF_INET6:
+               inet_ntop(AF_INET6, &su->sin6.sin6_addr, cbuf, sizeof(cbuf));
+               ret += bputs(buf, cbuf);
+               if (include_port)
+                       ret += bprintfrr(buf, ":%d", su->sin6.sin6_port);
+               break;
+       default:
+               ret += bprintfrr(buf, "(af %d)", sockunion_family(su));
        }
 
-       return consumed;
+       return ret;
 }
 
 int sockunion_is_null(const union sockunion *su)
index a115507192ccfac9b1f7287c961652f1be4bf2c3..ca48d56e4d72decc74b04238c2dc56ae07dc3e35 100644 (file)
@@ -307,20 +307,20 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size)
 }
 
 printfrr_ext_autoreg_p("RN", printfrr_rn)
-static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt,
+static ssize_t printfrr_rn(struct fbuf *buf, const char **fmt,
                           int prec, const void *ptr)
 {
        const struct route_node *rn = ptr;
        const struct prefix *dst_p, *src_p;
+       char cbuf[PREFIX_STRLEN * 2 + 6];
 
-       if (rn) {
-               srcdest_rnode_prefixes(rn, &dst_p, &src_p);
-               srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz);
-       } else {
-               strlcpy(buf, "NULL", bsz);
-       }
+       if (!rn)
+               return bputs(buf, "NULL");
 
-       return 2;
+       srcdest_rnode_prefixes(rn, &dst_p, &src_p);
+       srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p,
+                   cbuf, sizeof(cbuf));
+       return bputs(buf, cbuf);
 }
 
 struct route_table *srcdest_srcnode_table(struct route_node *rn)
index 24de3fa88d0d88720d7a8dedb7dda11babee1b1e..9ffea8b9782d8acf8a1797b7b9d26ee58f8e97ba 100644 (file)
@@ -64,8 +64,37 @@ static void printchk(const char *ref, const char *fmt, ...)
 {
        va_list ap;
        char bufrr[256];
+       bool truncfail = false;
+       size_t i;
+       size_t expectlen;
+
        memset(bufrr, 0xcc, sizeof(bufrr));
 
+       va_start(ap, fmt);
+       expectlen = vsnprintfrr(NULL, 0, fmt, ap);
+       va_end(ap);
+
+       va_start(ap, fmt);
+       vsnprintfrr(bufrr, 7, fmt, ap);
+       va_end(ap);
+
+       if (strnlen(bufrr, 7) == 7)
+               truncfail = true;
+       if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0)
+               truncfail = true;
+       for (i = 7; i < sizeof(bufrr); i++)
+               if (bufrr[i] != (char)0xcc) {
+                       truncfail = true;
+                       break;
+               }
+
+       if (truncfail) {
+               printf("truncation test FAILED:\n"
+                      "fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n",
+                      fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
+               errors++;
+       }
+
        va_start(ap, fmt);
        vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap);
        va_end(ap);
@@ -74,6 +103,10 @@ static void printchk(const char *ref, const char *fmt, ...)
               fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok");
        if (strcmp(ref, bufrr))
                errors++;
+       if (strlen(bufrr) != expectlen) {
+               printf("return value <> length mismatch\n");
+               errors++;
+       }
 }
 
 int main(int argc, char **argv)
@@ -112,6 +145,7 @@ int main(int argc, char **argv)
        inet_aton("192.168.1.2", &ip);
        printchk("192.168.1.2", "%pI4", &ip);
        printchk("         192.168.1.2", "%20pI4", &ip);
+       printchk("192.168.1.2         ", "%-20pI4", &ip);
 
        printcmp("%p", &ip);