summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_table.c2
-rw-r--r--doc/developer/logging.rst144
-rw-r--r--lib/monotime.h75
-rw-r--r--lib/nexthop.c2
-rw-r--r--lib/prefix.c58
-rw-r--r--lib/printf/glue.c4
-rw-r--r--lib/printfrr.h14
-rw-r--r--lib/sockunion.c6
-rw-r--r--lib/srcdest_table.c2
-rw-r--r--lib/strformat.c333
-rw-r--r--lib/thread.c67
-rw-r--r--lib/thread.h4
-rw-r--r--pimd/pim_addr.c4
-rw-r--r--tests/lib/test_printfrr.c114
-rwxr-xr-xtools/checkpatch.pl2
15 files changed, 806 insertions, 25 deletions
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 4ed8c7c59b..0ca78d0bb6 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -237,7 +237,7 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table,
return matched;
}
-printfrr_ext_autoreg_p("BD", printfrr_bd)
+printfrr_ext_autoreg_p("BD", printfrr_bd);
static ssize_t printfrr_bd(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst
index e608046820..4e6fc04206 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -123,10 +123,14 @@ Networking data types
:frrfmtout:`1.2.3.4`
+ ``%pI4s``: :frrfmtout:`*` — print star instead of ``0.0.0.0`` (for multicast)
+
.. frrfmt:: %pI6 (struct in6_addr *)
:frrfmtout:`fe80::1234`
+ ``%pI6s``: :frrfmtout:`*` — print star instead of ``::`` (for multicast)
+
.. frrfmt:: %pEA (struct ethaddr *)
:frrfmtout:`01:23:45:67:89:ab`
@@ -135,6 +139,8 @@ Networking data types
:frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234`
+ ``%pIAs``: — print star instead of zero address (for multicast)
+
.. frrfmt:: %pFX (struct prefix *)
:frrfmtout:`1.2.3.0/24` / :frrfmtout:`fe80::1234/64`
@@ -213,6 +219,144 @@ Networking data types
:frrfmtout:`SOCK_STREAM`
+Time/interval formats
+^^^^^^^^^^^^^^^^^^^^^
+
+.. frrfmt:: %pTS (struct timespec *)
+
+.. frrfmt:: %pTV (struct timeval *)
+
+.. frrfmt:: %pTT (time_t *)
+
+ Above 3 options internally result in the same code being called, support
+ the same flags and produce equal output with one exception: ``%pTT``
+ has no sub-second precision and the formatter will never print a
+ (nonsensical) ``.000``.
+
+ Exactly one of ``I``, ``M`` or ``R`` must immediately follow after
+ ``TS``/``TV``/``TT`` to specify whether the input is an interval, monotonic
+ timestamp or realtime timestamp:
+
+ ``%pTVI``: input is an interval, not a timestamp. Print interval.
+
+ ``%pTVIs``: input is an interval, convert to wallclock by subtracting it
+ from current time (i.e. interval has passed **s**\ ince.)
+
+ ``%pTVIu``: input is an interval, convert to wallclock by adding it to
+ current time (i.e. **u**\ ntil interval has passed.)
+
+ ``%pTVM`` - input is a timestamp on CLOCK_MONOTONIC, convert to wallclock
+ time (by grabbing current CLOCK_MONOTONIC and CLOCK_REALTIME and doing the
+ math) and print calendaric date.
+
+ ``%pTVMs`` - input is a timestamp on CLOCK_MONOTONIC, print interval
+ **s**\ ince that timestamp (elapsed.)
+
+ ``%pTVMu`` - input is a timestamp on CLOCK_MONOTONIC, print interval
+ **u**\ ntil that timestamp (deadline.)
+
+ ``%pTVR`` - input is a timestamp on CLOCK_REALTIME, print calendaric date.
+
+ ``%pTVRs`` - input is a timestamp on CLOCK_REALTIME, print interval
+ **s**\ ince that timestamp.
+
+ ``%pTVRu`` - input is a timestamp on CLOCK_REALTIME, print interval
+ **u**\ ntil that timestamp.
+
+ ``%pTVA`` - reserved for CLOCK_TAI in case a PTP implementation is
+ interfaced to FRR. Not currently implemented.
+
+ .. note::
+
+ If ``%pTVRs`` or ``%pTVRu`` are used, this is generally an indication
+ that a CLOCK_MONOTONIC timestamp should be used instead (or added in
+ parallel.) CLOCK_REALTIME might be adjusted by NTP, PTP or similar
+ procedures, causing bogus intervals to be printed.
+
+ ``%pTVM`` on first look might be assumed to have the same problem, but
+ on closer thought the assumption is always that current system time is
+ correct. And since a CLOCK_MONOTONIC interval is also quite safe to
+ assume to be correct, the (past) absolute timestamp to be printed from
+ this can likely be correct even if it doesn't match what CLOCK_REALTIME
+ would have indicated at that point in the past. This logic does,
+ however, not quite work for *future* times.
+
+ Generally speaking, almost all use cases in FRR should (and do) use
+ CLOCK_MONOTONIC (through :c:func:`monotime()`.)
+
+ Flags common to printing calendar times and intervals:
+
+ ``p``: include spaces in appropriate places (depends on selected format.)
+
+ ``%p.3TV...``: specify sub-second resolution (use with ``FMT_NSTD`` to
+ suppress gcc warning.) As noted above, ``%pTT`` will never print sub-second
+ digits since there are none. Only some formats support printing sub-second
+ digits and the default may vary.
+
+ The following flags are available for printing calendar times/dates:
+
+ (no flag): :frrfmtout:`Sat Jan 1 00:00:00 2022` - print output from
+ ``ctime()``, in local time zone. Since FRR does not currently use/enable
+ locale support, this is always the C locale. (Locale support getting added
+ is unlikely for the time being and would likely break other things worse
+ than this.)
+
+ ``i``: :frrfmtout:`2022-01-01T00:00:00.123` - ISO8601 timestamp in local
+ time zone (note there is no ``Z`` or ``+00:00`` suffix.) Defaults to
+ millisecond precision.
+
+ ``ip``: :frrfmtout:`2022-01-01 00:00:00.123` - use readable form of ISO8601
+ with space instead of ``T`` separator.
+
+ The following flags are available for printing intervals:
+
+ (no flag): :frrfmtout:`9w9d09:09:09.123` - does not match any
+ preexisting format; added because it does not lose precision (like ``t``)
+ for longer intervals without printing huge numbers (like ``h``/``m``).
+ Defaults to millisecond precision. The week/day fields are left off if
+ they're zero, ``p`` adds a space after the respective letter.
+
+ ``t``: :frrfmtout:`9w9d09h`, :frrfmtout:`9d09h09m`, :frrfmtout:`09:09:09` -
+ this replaces :c:func:`frrtime_to_interval()`. ``p`` adds spaces after
+ week/day/hour letters.
+
+ ``d``: print decimal number of seconds. Defaults to millisecond precision.
+
+ ``x`` / ``tx`` / ``dx``: Like no flag / ``t`` / ``d``, but print
+ :frrfmtout:`-` for zero or negative intervals (for use with unset timers.)
+
+ ``h``: :frrfmtout:`09:09:09`
+
+ ``hx``: :frrfmtout:`09:09:09`, :frrfmtout:`--:--:--` - this replaces
+ :c:func:`pim_time_timer_to_hhmmss()`.
+
+ ``m``: :frrfmtout:`09:09`
+
+ ``mx``: :frrfmtout:`09:09`, :frrfmtout:`--:--` - this replaces
+ :c:func:`pim_time_timer_to_mmss()`.
+
+FRR library helper formats
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. frrfmt:: %pTH (struct thread *)
+
+ Print remaining time on timer thread. Interval-printing flag characters
+ listed above for ``%pTV`` can be added, e.g. ``%pTHtx``.
+
+ ``NULL`` pointers are printed as ``-``.
+
+.. frrfmt:: %pTHD (struct thread *)
+
+ Print debugging information for given thread. Sample output:
+
+ .. code-block:: none
+
+ {(thread *)NULL}
+ {(thread *)0x55a3b5818910 arg=0x55a3b5827c50 timer r=7.824 mld_t_query() &mld_ifp->t_query from pimd/pim6_mld.c:1369}
+ {(thread *)0x55a3b5827230 arg=0x55a3b5827c50 read fd=16 mld_t_recv() &mld_ifp->t_recv from pimd/pim6_mld.c:1186}
+
+ (The output is aligned to some degree.)
+
General utility formats
^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/lib/monotime.h b/lib/monotime.h
index dda763784f..15b6933955 100644
--- a/lib/monotime.h
+++ b/lib/monotime.h
@@ -25,6 +25,9 @@
extern "C" {
#endif
+struct fbuf;
+struct printfrr_eargs;
+
#ifndef TIMESPEC_TO_TIMEVAL
/* should be in sys/time.h on BSD & Linux libcs */
#define TIMESPEC_TO_TIMEVAL(tv, ts) \
@@ -42,6 +45,31 @@ extern "C" {
} while (0)
#endif
+/* Linux/glibc is sadly missing these timespec helpers */
+#ifndef timespecadd
+#define timespecadd(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec >= 1000000000L) { \
+ (vsp)->tv_sec++; \
+ (vsp)->tv_nsec -= 1000000000L; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timespecsub
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+#endif
+
static inline time_t monotime(struct timeval *tvo)
{
struct timespec ts;
@@ -132,6 +160,53 @@ static inline const char *frrtime_to_interval(time_t t, char *buf,
return buf;
}
+enum {
+ /* n/a - input was seconds precision, don't print any fractional */
+ TIMEFMT_SECONDS = (1 << 0),
+ /* caller is directly invoking printfrr_time and has pre-specified
+ * I/Iu/Is/M/Mu/Ms/R/Ru/Rs (for printing timers)
+ */
+ TIMEFMT_PRESELECT = (1 << 1),
+ /* don't print any output - this is needed for invoking printfrr_time
+ * from another printfrr extensions to skip over flag characters
+ */
+ TIMEFMT_SKIP = (1 << 2),
+ /* use spaces in appropriate places */
+ TIMEFMT_SPACE = (1 << 3),
+
+ /* input interpretations: */
+ TIMEFMT_REALTIME = (1 << 8),
+ TIMEFMT_MONOTONIC = (1 << 9),
+ TIMEFMT_SINCE = (1 << 10),
+ TIMEFMT_UNTIL = (1 << 11),
+
+ TIMEFMT_ABSOLUTE = TIMEFMT_REALTIME | TIMEFMT_MONOTONIC,
+ TIMEFMT_ANCHORS = TIMEFMT_SINCE | TIMEFMT_UNTIL,
+
+ /* calendaric formats: */
+ TIMEFMT_ISO8601 = (1 << 16),
+
+ /* interval formats: */
+ /* 't' - use [t]raditional 3-block format */
+ TIMEFMT_BASIC = (1 << 24),
+ /* 'm' - select mm:ss */
+ TIMEFMT_MMSS = (1 << 25),
+ /* 'h' - select hh:mm:ss */
+ TIMEFMT_HHMMSS = (1 << 26),
+ /* 'd' - print as decimal number of seconds */
+ TIMEFMT_DECIMAL = (1 << 27),
+ /* 'mx'/'hx' - replace zero value with "--:--" or "--:--:--" */
+ TIMEFMT_DASHES = (1 << 31),
+
+ /* helpers for reference */
+ TIMEFMT_TIMER_DEADLINE =
+ TIMEFMT_PRESELECT | TIMEFMT_MONOTONIC | TIMEFMT_UNTIL,
+ TIMEFMT_TIMER_INTERVAL = TIMEFMT_PRESELECT,
+};
+
+extern ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/nexthop.c b/lib/nexthop.c
index 2e09cb4bcc..e17eeb8303 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -943,7 +943,7 @@ int nexthop_str2backups(const char *str, int *num_backups,
* eth0
* (0-length if no interface present)
*/
-printfrr_ext_autoreg_p("NH", printfrr_nh)
+printfrr_ext_autoreg_p("NH", printfrr_nh);
static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
diff --git a/lib/prefix.c b/lib/prefix.c
index d3e8a612eb..90ab48a13b 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1353,7 +1353,7 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len)
return buf;
}
-printfrr_ext_autoreg_p("EA", printfrr_ea)
+printfrr_ext_autoreg_p("EA", printfrr_ea);
static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -1368,47 +1368,93 @@ static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea,
return bputs(buf, cbuf);
}
-printfrr_ext_autoreg_p("IA", printfrr_ia)
+printfrr_ext_autoreg_p("IA", printfrr_ia);
static ssize_t printfrr_ia(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
const struct ipaddr *ipa = ptr;
char cbuf[INET6_ADDRSTRLEN];
+ bool use_star = false;
+
+ if (ea->fmt[0] == 's') {
+ use_star = true;
+ ea->fmt++;
+ }
if (!ipa)
return bputs(buf, "(null)");
+ if (use_star) {
+ struct in_addr zero4 = {};
+ struct in6_addr zero6 = {};
+
+ switch (ipa->ipa_type) {
+ case IPADDR_V4:
+ if (!memcmp(&ipa->ip.addr, &zero4, sizeof(zero4)))
+ return bputch(buf, '*');
+ break;
+
+ case IPADDR_V6:
+ if (!memcmp(&ipa->ip.addr, &zero6, sizeof(zero6)))
+ return bputch(buf, '*');
+ break;
+
+ default:
+ break;
+ }
+ }
+
ipaddr2str(ipa, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
-printfrr_ext_autoreg_p("I4", printfrr_i4)
+printfrr_ext_autoreg_p("I4", printfrr_i4);
static ssize_t printfrr_i4(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
char cbuf[INET_ADDRSTRLEN];
+ bool use_star = false;
+ struct in_addr zero = {};
+
+ if (ea->fmt[0] == 's') {
+ use_star = true;
+ ea->fmt++;
+ }
if (!ptr)
return bputs(buf, "(null)");
+ if (use_star && !memcmp(ptr, &zero, sizeof(zero)))
+ return bputch(buf, '*');
+
inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
-printfrr_ext_autoreg_p("I6", printfrr_i6)
+printfrr_ext_autoreg_p("I6", printfrr_i6);
static ssize_t printfrr_i6(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
char cbuf[INET6_ADDRSTRLEN];
+ bool use_star = false;
+ struct in6_addr zero = {};
+
+ if (ea->fmt[0] == 's') {
+ use_star = true;
+ ea->fmt++;
+ }
if (!ptr)
return bputs(buf, "(null)");
+ if (use_star && !memcmp(ptr, &zero, sizeof(zero)))
+ return bputch(buf, '*');
+
inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
}
-printfrr_ext_autoreg_p("FX", printfrr_pfx)
+printfrr_ext_autoreg_p("FX", printfrr_pfx);
static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -1421,7 +1467,7 @@ static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
return bputs(buf, cbuf);
}
-printfrr_ext_autoreg_p("PSG4", printfrr_psg)
+printfrr_ext_autoreg_p("PSG4", printfrr_psg);
static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
diff --git a/lib/printf/glue.c b/lib/printf/glue.c
index 1147901236..6e39c2d9cf 100644
--- a/lib/printf/glue.c
+++ b/lib/printf/glue.c
@@ -256,7 +256,7 @@ ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea,
return -1;
}
-printfrr_ext_autoreg_p("FB", printfrr_fb)
+printfrr_ext_autoreg_p("FB", printfrr_fb);
static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -278,7 +278,7 @@ static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea,
return in->pos - in->buf;
}
-printfrr_ext_autoreg_p("VA", printfrr_va)
+printfrr_ext_autoreg_p("VA", printfrr_va);
static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
diff --git a/lib/printfrr.h b/lib/printfrr.h
index 37f1f9c8cd..a2d113ba1e 100644
--- a/lib/printfrr.h
+++ b/lib/printfrr.h
@@ -209,10 +209,11 @@ void printfrr_ext_reg(const struct printfrr_ext *);
.print_ptr = print_fn, \
}; \
static void _printreg_##print_fn(void) __attribute__((constructor)); \
- static void _printreg_##print_fn(void) { \
+ static void _printreg_##print_fn(void) \
+ { \
printfrr_ext_reg(&_printext_##print_fn); \
} \
- /* end */
+ MACRO_REQUIRE_SEMICOLON()
#define printfrr_ext_autoreg_i(matchs, print_fn) \
static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \
@@ -222,10 +223,11 @@ void printfrr_ext_reg(const struct printfrr_ext *);
.print_int = print_fn, \
}; \
static void _printreg_##print_fn(void) __attribute__((constructor)); \
- static void _printreg_##print_fn(void) { \
+ static void _printreg_##print_fn(void) \
+ { \
printfrr_ext_reg(&_printext_##print_fn); \
} \
- /* end */
+ MACRO_REQUIRE_SEMICOLON()
/* fbuf helper functions - note all 3 of these return the length that would
* be written regardless of how much space was available in the buffer, as
@@ -286,6 +288,10 @@ struct va_format {
#pragma FRR printfrr_ext "%pSE" (char *)
#pragma FRR printfrr_ext "%pSQ" (char *)
+
+#pragma FRR printfrr_ext "%pTS" (struct timespec *)
+#pragma FRR printfrr_ext "%pTV" (struct timeval *)
+#pragma FRR printfrr_ext "%pTT" (time_t *)
#endif
/* when using non-ISO-C compatible extension specifiers... */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index c7af458e9e..006ac142aa 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -662,7 +662,7 @@ void sockunion_init(union sockunion *su)
memset(su, 0, sizeof(union sockunion));
}
-printfrr_ext_autoreg_p("SU", printfrr_psu)
+printfrr_ext_autoreg_p("SU", printfrr_psu);
static ssize_t printfrr_psu(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -752,7 +752,7 @@ int sockunion_is_null(const union sockunion *su)
}
}
-printfrr_ext_autoreg_i("PF", printfrr_pf)
+printfrr_ext_autoreg_i("PF", printfrr_pf);
static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea,
uintmax_t val)
{
@@ -775,7 +775,7 @@ static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea,
return bprintfrr(buf, "AF_(%ju)", val);
}
-printfrr_ext_autoreg_i("SO", printfrr_so)
+printfrr_ext_autoreg_i("SO", printfrr_so);
static ssize_t printfrr_so(struct fbuf *buf, struct printfrr_eargs *ea,
uintmax_t val)
{
diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c
index d2e0682e95..828cf2cce0 100644
--- a/lib/srcdest_table.c
+++ b/lib/srcdest_table.c
@@ -306,7 +306,7 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size)
return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size);
}
-printfrr_ext_autoreg_p("RN", printfrr_rn)
+printfrr_ext_autoreg_p("RN", printfrr_rn);
static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
diff --git a/lib/strformat.c b/lib/strformat.c
index 431e573a0c..a420ba553a 100644
--- a/lib/strformat.c
+++ b/lib/strformat.c
@@ -22,10 +22,12 @@
#include <string.h>
#include <ctype.h>
+#include <time.h>
#include "printfrr.h"
+#include "monotime.h"
-printfrr_ext_autoreg_p("HX", printfrr_hexdump)
+printfrr_ext_autoreg_p("HX", printfrr_hexdump);
static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -56,7 +58,7 @@ static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea,
/* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */
-printfrr_ext_autoreg_p("HS", printfrr_hexdstr)
+printfrr_ext_autoreg_p("HS", printfrr_hexdstr);
static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
@@ -199,7 +201,7 @@ static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len,
return ret;
}
-printfrr_ext_autoreg_p("SE", printfrr_escape)
+printfrr_ext_autoreg_p("SE", printfrr_escape);
static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea,
const void *vptr)
{
@@ -224,7 +226,7 @@ static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea,
return bquote(buf, ptr, len, ESC_ALL);
}
-printfrr_ext_autoreg_p("SQ", printfrr_quote)
+printfrr_ext_autoreg_p("SQ", printfrr_quote);
static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea,
const void *vptr)
{
@@ -270,3 +272,326 @@ static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea,
ret += bputch(buf, '"');
return ret;
}
+
+static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags);
+static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags);
+
+ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags)
+{
+ bool have_abs, have_anchor;
+
+ if (!(flags & TIMEFMT_PRESELECT)) {
+ switch (ea->fmt[0]) {
+ case 'I':
+ /* no bit set */
+ break;
+ case 'M':
+ flags |= TIMEFMT_MONOTONIC;
+ break;
+ case 'R':
+ flags |= TIMEFMT_REALTIME;
+ break;
+ default:
+ return bputs(buf,
+ "{invalid time format input specifier}");
+ }
+ ea->fmt++;
+
+ if (ea->fmt[0] == 's') {
+ flags |= TIMEFMT_SINCE;
+ ea->fmt++;
+ } else if (ea->fmt[0] == 'u') {
+ flags |= TIMEFMT_UNTIL;
+ ea->fmt++;
+ }
+ }
+
+ have_abs = !!(flags & TIMEFMT_ABSOLUTE);
+ have_anchor = !!(flags & TIMEFMT_ANCHORS);
+
+ if (have_abs ^ have_anchor)
+ return printfrr_abstime(buf, ea, ts, flags);
+ else
+ return printfrr_reltime(buf, ea, ts, flags);
+}
+
+static ssize_t do_subsec(struct fbuf *buf, const struct timespec *ts,
+ int precision, unsigned int flags)
+{
+ unsigned long long frac;
+
+ if (precision <= 0 || (flags & TIMEFMT_SECONDS))
+ return 0;
+
+ frac = ts->tv_nsec;
+ if (precision > 9)
+ precision = 9;
+ for (int i = precision; i < 9; i++)
+ frac /= 10;
+ return bprintfrr(buf, ".%0*llu", precision, frac);
+}
+
+static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags)
+{
+ struct timespec real_ts[1];
+ struct tm tm;
+ char cbuf[32] = ""; /* manpage says 26 for ctime_r */
+ ssize_t ret = 0;
+ int precision = ea->precision;
+
+ while (ea->fmt[0]) {
+ char ch = *ea->fmt++;
+
+ switch (ch) {
+ case 'p':
+ flags |= TIMEFMT_SPACE;
+ continue;
+ case 'i':
+ flags |= TIMEFMT_ISO8601;
+ continue;
+ }
+
+ ea->fmt--;
+ break;
+ }
+
+ if (flags & TIMEFMT_SKIP)
+ return 0;
+
+ if (flags & TIMEFMT_REALTIME)
+ *real_ts = *ts;
+ else if (flags & TIMEFMT_MONOTONIC) {
+ struct timespec mono_now[1];
+
+ clock_gettime(CLOCK_REALTIME, real_ts);
+ clock_gettime(CLOCK_MONOTONIC, mono_now);
+
+ timespecsub(real_ts, mono_now, real_ts);
+ timespecadd(real_ts, ts, real_ts);
+ } else {
+ clock_gettime(CLOCK_REALTIME, real_ts);
+
+ if (flags & TIMEFMT_SINCE)
+ timespecsub(real_ts, ts, real_ts);
+ else /* flags & TIMEFMT_UNTIL */
+ timespecadd(real_ts, ts, real_ts);
+ }
+
+ localtime_r(&real_ts->tv_sec, &tm);
+
+ if (flags & TIMEFMT_ISO8601) {
+ if (flags & TIMEFMT_SPACE)
+ strftime(cbuf, sizeof(cbuf), "%Y-%m-%d %H:%M:%S", &tm);
+ else
+ strftime(cbuf, sizeof(cbuf), "%Y-%m-%dT%H:%M:%S", &tm);
+ ret += bputs(buf, cbuf);
+
+ if (precision == -1)
+ precision = 3;
+ ret += do_subsec(buf, real_ts, precision, flags);
+ } else {
+ size_t len;
+
+ asctime_r(&tm, cbuf);
+
+ len = strlen(cbuf);
+ if (!len)
+ /* WTF. */
+ return 0;
+ if (cbuf[len - 1] == '\n')
+ cbuf[len - 1] = '\0';
+
+ ret += bputs(buf, cbuf);
+ }
+ return ret;
+}
+
+static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct timespec *ts, unsigned int flags)
+{
+ struct timespec real_ts[1];
+ ssize_t ret = 0;
+ const char *space = "";
+ const char *dashes = "-";
+ int precision = ea->precision;
+
+ while (ea->fmt[0]) {
+ char ch = *ea->fmt++;
+
+ switch (ch) {
+ case 'p':
+ flags |= TIMEFMT_SPACE;
+ space = " ";
+ continue;
+ case 't':
+ flags |= TIMEFMT_BASIC;
+ continue;
+ case 'd':
+ flags |= TIMEFMT_DECIMAL;
+ continue;
+ case 'm':
+ flags |= TIMEFMT_MMSS;
+ dashes = "--:--";
+ continue;
+ case 'h':
+ flags |= TIMEFMT_HHMMSS;
+ dashes = "--:--:--";
+ continue;
+ case 'x':
+ flags |= TIMEFMT_DASHES;
+ continue;
+ }
+
+ ea->fmt--;
+ break;
+ }
+
+ if (flags & TIMEFMT_SKIP)
+ return 0;
+
+ if (flags & TIMEFMT_ABSOLUTE) {
+ struct timespec anchor[1];
+
+ if (flags & TIMEFMT_REALTIME)
+ clock_gettime(CLOCK_REALTIME, anchor);
+ else
+ clock_gettime(CLOCK_MONOTONIC, anchor);
+ if (flags & TIMEFMT_UNTIL)
+ timespecsub(ts, anchor, real_ts);
+ else /* flags & TIMEFMT_SINCE */
+ timespecsub(anchor, ts, real_ts);
+ } else
+ *real_ts = *ts;
+
+ if (real_ts->tv_sec == 0 && real_ts->tv_nsec == 0 &&
+ (flags & TIMEFMT_DASHES))
+ return bputs(buf, dashes);
+
+ if (real_ts->tv_sec < 0) {
+ if (flags & TIMEFMT_DASHES)
+ return bputs(buf, dashes);
+
+ /* -0.3s is { -1s + 700ms } */
+ real_ts->tv_sec = -real_ts->tv_sec - 1;
+ real_ts->tv_nsec = 1000000000L - real_ts->tv_nsec;
+ if (real_ts->tv_nsec >= 1000000000L) {
+ real_ts->tv_sec++;
+ real_ts->tv_nsec -= 1000000000L;
+ }
+
+ /* all formats have a - make sense in front */
+ ret += bputch(buf, '-');
+ }
+
+ if (flags & TIMEFMT_DECIMAL) {
+ ret += bprintfrr(buf, "%lld", (long long)real_ts->tv_sec);
+ if (precision == -1)
+ precision = 3;
+ ret += do_subsec(buf, real_ts, precision, flags);
+ return ret;
+ }
+
+ /* these divisions may be slow on embedded boxes, hence only do the
+ * ones we need, plus the ?: zero check to hopefully skip zeros fast
+ */
+ lldiv_t min_sec = lldiv(real_ts->tv_sec, 60);
+
+ if (flags & TIMEFMT_MMSS) {
+ ret += bprintfrr(buf, "%02lld:%02lld", min_sec.quot,
+ min_sec.rem);
+ ret += do_subsec(buf, real_ts, precision, flags);
+ return ret;
+ }
+
+ lldiv_t hour_min = min_sec.quot ? lldiv(min_sec.quot, 60) : (lldiv_t){};
+
+ if (flags & TIMEFMT_HHMMSS) {
+ ret += bprintfrr(buf, "%02lld:%02lld:%02lld", hour_min.quot,
+ hour_min.rem, min_sec.rem);
+ ret += do_subsec(buf, real_ts, precision, flags);
+ return ret;
+ }
+
+ lldiv_t day_hour =
+ hour_min.quot ? lldiv(hour_min.quot, 24) : (lldiv_t){};
+ lldiv_t week_day =
+ day_hour.quot ? lldiv(day_hour.quot, 7) : (lldiv_t){};
+
+ /* if sub-second precision is not supported, return */
+ if (flags & TIMEFMT_BASIC) {
+ /* match frrtime_to_interval (without space flag) */
+ if (week_day.quot)
+ ret += bprintfrr(buf, "%lldw%s%lldd%s%02lldh",
+ week_day.quot, space, week_day.rem,
+ space, day_hour.rem);
+ else if (day_hour.quot)
+ ret += bprintfrr(buf, "%lldd%s%02lldh%s%02lldm",
+ day_hour.quot, space, day_hour.rem,
+ space, hour_min.rem);
+ else
+ ret += bprintfrr(buf, "%02lld:%02lld:%02lld",
+ hour_min.quot, hour_min.rem,
+ min_sec.rem);
+ /* no sub-seconds here */
+ return ret;
+ }
+
+ /* default format */
+ if (week_day.quot)
+ ret += bprintfrr(buf, "%lldw%s", week_day.quot, space);
+ if (week_day.rem || week_day.quot)
+ ret += bprintfrr(buf, "%lldd%s", week_day.rem, space);
+
+ ret += bprintfrr(buf, "%02lld:%02lld:%02lld", day_hour.rem,
+ hour_min.rem, min_sec.rem);
+
+ if (precision == -1)
+ precision = 3;
+ ret += do_subsec(buf, real_ts, precision, flags);
+ return ret;
+}
+
+printfrr_ext_autoreg_p("TS", printfrr_ts);
+static ssize_t printfrr_ts(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const struct timespec *ts = vptr;
+
+ if (!ts)
+ return bputs(buf, "(null)");
+ return printfrr_time(buf, ea, ts, 0);
+}
+
+printfrr_ext_autoreg_p("TV", printfrr_tv);
+static ssize_t printfrr_tv(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const struct timeval *tv = vptr;
+ struct timespec ts;
+
+ if (!tv)
+ return bputs(buf, "(null)");
+
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+ return printfrr_time(buf, ea, &ts, 0);
+}
+
+printfrr_ext_autoreg_p("TT", printfrr_tt);
+static ssize_t printfrr_tt(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *vptr)
+{
+ const time_t *tt = vptr;
+ struct timespec ts;
+
+ if (!tt)
+ return bputs(buf, "(null)");
+
+ ts.tv_sec = *tt;
+ ts.tv_nsec = 0;
+ return printfrr_time(buf, ea, &ts, TIMEFMT_SECONDS);
+}
diff --git a/lib/thread.c b/lib/thread.c
index 7b223ed6de..77e34f48f3 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -2056,3 +2056,70 @@ bool thread_is_scheduled(struct thread *thread)
return true;
}
+
+static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea,
+ const struct thread *thread)
+{
+ static const char * const types[] = {
+ [THREAD_READ] = "read",
+ [THREAD_WRITE] = "write",
+ [THREAD_TIMER] = "timer",
+ [THREAD_EVENT] = "event",
+ [THREAD_READY] = "ready",
+ [THREAD_UNUSED] = "unused",
+ [THREAD_EXECUTE] = "exec",
+ };
+ ssize_t rv = 0;
+ char info[16] = "";
+
+ if (!thread)
+ return bputs(buf, "{(thread *)NULL}");
+
+ rv += bprintfrr(buf, "{(thread *)%p arg=%p", thread, thread->arg);
+
+ if (thread->type < array_size(types) && types[thread->type])
+ rv += bprintfrr(buf, " %-6s", types[thread->type]);
+ else
+ rv += bprintfrr(buf, " INVALID(%u)", thread->type);
+
+ switch (thread->type) {
+ case THREAD_READ:
+ case THREAD_WRITE:
+ snprintfrr(info, sizeof(info), "fd=%d", thread->u.fd);
+ break;
+
+ case THREAD_TIMER:
+ snprintfrr(info, sizeof(info), "r=%pTVMud", &thread->u.sands);
+ break;
+ }
+
+ rv += bprintfrr(buf, " %-12s %s() %s from %s:%d}", info,
+ thread->xref->funcname, thread->xref->dest,
+ thread->xref->xref.file, thread->xref->xref.line);
+ return rv;
+}
+
+printfrr_ext_autoreg_p("TH", printfrr_thread);
+static ssize_t printfrr_thread(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct thread *thread = ptr;
+ struct timespec remain = {};
+
+ if (ea->fmt[0] == 'D') {
+ ea->fmt++;
+ return printfrr_thread_dbg(buf, ea, thread);
+ }
+
+ if (!thread) {
+ /* need to jump over time formatting flag characters in the
+ * input format string, i.e. adjust ea->fmt!
+ */
+ printfrr_time(buf, ea, &remain,
+ TIMEFMT_TIMER_DEADLINE | TIMEFMT_SKIP);
+ return bputch(buf, '-');
+ }
+
+ TIMEVAL_TO_TIMESPEC(&thread->u.sands, &remain);
+ return printfrr_time(buf, ea, &remain, TIMEFMT_TIMER_DEADLINE);
+}
diff --git a/lib/thread.h b/lib/thread.h
index 39f21da11d..49a70696d0 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -128,6 +128,10 @@ struct thread {
pthread_mutex_t mtx; /* mutex for thread.c functions */
};
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pTH" (struct thread *)
+#endif
+
struct cpu_thread_history {
int (*func)(struct thread *);
atomic_size_t total_cpu_warn;
diff --git a/pimd/pim_addr.c b/pimd/pim_addr.c
index 825f38274a..8d89b57141 100644
--- a/pimd/pim_addr.c
+++ b/pimd/pim_addr.c
@@ -24,7 +24,7 @@
#include "prefix.h"
-printfrr_ext_autoreg_p("PA", printfrr_pimaddr)
+printfrr_ext_autoreg_p("PA", printfrr_pimaddr);
static ssize_t printfrr_pimaddr(struct fbuf *buf, struct printfrr_eargs *ea,
const void *vptr)
{
@@ -56,7 +56,7 @@ static ssize_t printfrr_pimaddr(struct fbuf *buf, struct printfrr_eargs *ea,
#endif
}
-printfrr_ext_autoreg_p("SG", printfrr_sgaddr)
+printfrr_ext_autoreg_p("SG", printfrr_sgaddr);
static ssize_t printfrr_sgaddr(struct fbuf *buf, struct printfrr_eargs *ea,
const void *vptr)
{
diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c
index 7694077574..8f9d637afd 100644
--- a/tests/lib/test_printfrr.c
+++ b/tests/lib/test_printfrr.c
@@ -186,6 +186,10 @@ int main(int argc, char **argv)
test_va("VA [192.168.1.2 1234] --", "%pI4 %u", &ip, 1234);
+ inet_aton("0.0.0.0", &ip);
+ printchk("0.0.0.0", "%pI4", &ip);
+ printchk("*", "%pI4s", &ip);
+
snprintfrr(buf, sizeof(buf), "test%s", "#1");
csnprintfrr(buf, sizeof(buf), "test%s", "#2");
assert(strcmp(buf, "test#1test#2") == 0);
@@ -274,5 +278,115 @@ int main(int argc, char **argv)
inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6);
printchk("fe2c::34", "%pNHcg", &nh);
+ /* time printing */
+
+ /* need a non-UTC timezone for testing */
+ setenv("TZ", "TEST-01:00", 1);
+ tzset();
+
+ struct timespec ts;
+ struct timeval tv;
+ time_t tt;
+
+ ts.tv_sec = tv.tv_sec = tt = 1642015880;
+ ts.tv_nsec = 123456789;
+ tv.tv_usec = 234567;
+
+ printchk("Wed Jan 12 20:31:20 2022", "%pTSR", &ts);
+ printchk("Wed Jan 12 20:31:20 2022", "%pTVR", &tv);
+ printchk("Wed Jan 12 20:31:20 2022", "%pTTR", &tt);
+
+ FMT_NSTD(printchk("Wed Jan 12 20:31:20 2022", "%.3pTSR", &ts));
+
+ printchk("2022-01-12T20:31:20.123", "%pTSRi", &ts);
+ printchk("2022-01-12 20:31:20.123", "%pTSRip", &ts);
+ printchk("2022-01-12 20:31:20.123", "%pTSRpi", &ts);
+ FMT_NSTD(printchk("2022-01-12T20:31:20", "%.0pTSRi", &ts));
+ FMT_NSTD(printchk("2022-01-12T20:31:20.123456789", "%.9pTSRi", &ts));
+ FMT_NSTD(printchk("2022-01-12T20:31:20", "%.3pTTRi", &tt));
+
+ ts.tv_sec = tv.tv_sec = tt = 9 * 86400 + 12345;
+
+ printchk("1w 2d 03:25:45.123", "%pTSIp", &ts);
+ printchk("1w2d03:25:45.123", "%pTSI", &ts);
+ printchk("1w2d03:25:45.234", "%pTVI", &tv);
+ printchk("1w2d03:25:45", "%pTTI", &tt);
+
+ printchk("1w 2d 03h", "%pTVItp", &tv);
+ printchk("1w2d03h", "%pTSIt", &ts);
+
+ printchk("219:25:45", "%pTVIh", &tv);
+ printchk("13165:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 1 * 86400 + 12345;
+
+ printchk("1d 03:25:45.123", "%pTSIp", &ts);
+ printchk("1d03:25:45.234", "%pTVI", &tv);
+
+ printchk("1d 03h 25m", "%pTVItp", &tv);
+ printchk("1d03h25m", "%pTSIt", &ts);
+
+ printchk("98745.234", "%pTVId", &tv);
+
+ printchk("27:25:45", "%pTVIh", &tv);
+ printchk("1645:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 12345;
+
+ printchk("03:25:45.123", "%pTSIp", &ts);
+ printchk("03:25:45.123", "%pTSI", &ts);
+ printchk("03:25:45.234", "%pTVI", &tv);
+ printchk("03:25:45", "%pTTI", &tt);
+
+ printchk("03:25:45", "%pTSItp", &ts);
+ printchk("03:25:45", "%pTVIt", &tv);
+
+ printchk("12345.234", "%pTVId", &tv);
+
+ printchk("03:25:45", "%pTVIh", &tv);
+ printchk("205:45", "%pTVIm", &tv);
+
+ ts.tv_sec = tv.tv_sec = tt = 0;
+
+ printchk("00:00:00.123", "%pTSIp", &ts);
+ printchk("00:00:00.123", "%pTSI", &ts);
+ printchk("00:00:00.234", "%pTVI", &tv);
+ printchk("00:00:00", "%pTTI", &tt);
+
+ printchk("00:00:00", "%pTVItp", &tv);
+ printchk("00:00:00", "%pTSIt", &ts);
+
+ printchk("0.234", "%pTVId", &tv);
+ printchk("0.234", "%pTVIdx", &tv);
+ printchk("-", "%pTTIdx", &tt);
+
+ printchk("00:00:00", "%pTVIhx", &tv);
+ printchk("--:--:--", "%pTTIhx", &tt);
+ printchk("00:00", "%pTVImx", &tv);
+ printchk("--:--", "%pTTImx", &tt);
+
+ ts.tv_sec = tv.tv_sec = tt = -10;
+
+ printchk("-00:00:09.876", "%pTSIp", &ts);
+ printchk("-00:00:09.876", "%pTSI", &ts);
+ printchk("-00:00:09.765", "%pTVI", &tv);
+ printchk("-00:00:10", "%pTTI", &tt);
+
+ printchk("-00:00:09", "%pTSItp", &ts);
+ printchk("-00:00:09", "%pTSIt", &ts);
+ printchk("-00:00:09", "%pTVIt", &tv);
+ printchk("-00:00:10", "%pTTIt", &tt);
+
+ printchk("-9.765", "%pTVId", &tv);
+ printchk("-", "%pTVIdx", &tv);
+
+ printchk("-00:00:09", "%pTSIh", &ts);
+ printchk("--:--:--", "%pTVIhx", &tv);
+ printchk("--:--:--", "%pTTIhx", &tt);
+
+ printchk("-00:09", "%pTSIm", &ts);
+ printchk("--:--", "%pTVImx", &tv);
+ printchk("--:--", "%pTTImx", &tt);
+
return !!errors;
}
diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl
index d2eb20ce5b..db6460f343 100755
--- a/tools/checkpatch.pl
+++ b/tools/checkpatch.pl
@@ -5789,7 +5789,7 @@ sub process {
}
# check for vsprintf extension %p<foo> misuses
- if ($^V && $^V ge 5.10.0 &&
+ if (0 && $^V && $^V ge 5.10.0 &&
defined $stat &&
$stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
$1 !~ /^_*volatile_*$/) {