199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2020 Intel Corporation
399a2dd95SBruce Richardson */
499a2dd95SBruce Richardson
599a2dd95SBruce Richardson #ifndef _RTE_TELEMETRY_JSON_H_
699a2dd95SBruce Richardson #define _RTE_TELEMETRY_JSON_H_
799a2dd95SBruce Richardson
899a2dd95SBruce Richardson #include <inttypes.h>
999a2dd95SBruce Richardson #include <stdarg.h>
1099a2dd95SBruce Richardson #include <stdio.h>
112e6a781dSBruce Richardson #include <stdlib.h>
1299a2dd95SBruce Richardson #include <rte_common.h>
13b7673168SGowrishankar Muthukrishnan #include <rte_telemetry.h>
1499a2dd95SBruce Richardson
1599a2dd95SBruce Richardson /**
1699a2dd95SBruce Richardson * @file
1799a2dd95SBruce Richardson * Internal Telemetry Utility functions
1899a2dd95SBruce Richardson *
1999a2dd95SBruce Richardson * This file contains small inline functions to make it easier for applications
2099a2dd95SBruce Richardson * to build up valid JSON responses to telemetry requests.
21*3e4c5be9SThomas Monjalon */
2299a2dd95SBruce Richardson
2399a2dd95SBruce Richardson /**
2499a2dd95SBruce Richardson * @internal
2599a2dd95SBruce Richardson * Copies a value into a buffer if the buffer has enough available space.
267be78d02SJosh Soref * Nothing written to buffer if an overflow occurs.
27b7673168SGowrishankar Muthukrishnan * This function is not for use for values larger than given buffer length.
2899a2dd95SBruce Richardson */
2999a2dd95SBruce Richardson __rte_format_printf(3, 4)
3099a2dd95SBruce Richardson static inline int
__json_snprintf(char * buf,const int len,const char * format,...)3199a2dd95SBruce Richardson __json_snprintf(char *buf, const int len, const char *format, ...)
3299a2dd95SBruce Richardson {
3399a2dd95SBruce Richardson va_list ap;
342e6a781dSBruce Richardson char tmp[4];
352e6a781dSBruce Richardson char *newbuf;
3699a2dd95SBruce Richardson int ret;
3799a2dd95SBruce Richardson
382e6a781dSBruce Richardson if (len == 0)
392e6a781dSBruce Richardson return 0;
402e6a781dSBruce Richardson
412e6a781dSBruce Richardson /* to ensure unmodified if we overflow, we save off any values currently in buf
422e6a781dSBruce Richardson * before we printf, if they are short enough. We restore them on error.
432e6a781dSBruce Richardson */
442e6a781dSBruce Richardson if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
452e6a781dSBruce Richardson strcpy(tmp, buf); /* strcpy is safe as we know the length */
4699a2dd95SBruce Richardson va_start(ap, format);
472e6a781dSBruce Richardson ret = vsnprintf(buf, len, format, ap);
4899a2dd95SBruce Richardson va_end(ap);
492e6a781dSBruce Richardson if (ret > 0 && ret < len)
502e6a781dSBruce Richardson return ret;
512e6a781dSBruce Richardson strcpy(buf, tmp); /* restore on error */
522e6a781dSBruce Richardson return 0;
532e6a781dSBruce Richardson }
542e6a781dSBruce Richardson
552e6a781dSBruce Richardson /* in normal operations should never hit this, but can do if buffer is
562e6a781dSBruce Richardson * incorrectly initialized e.g. in unit test cases
572e6a781dSBruce Richardson */
582e6a781dSBruce Richardson newbuf = malloc(len);
592e6a781dSBruce Richardson if (newbuf == NULL)
602e6a781dSBruce Richardson return 0;
612e6a781dSBruce Richardson
622e6a781dSBruce Richardson va_start(ap, format);
632e6a781dSBruce Richardson ret = vsnprintf(newbuf, len, format, ap);
642e6a781dSBruce Richardson va_end(ap);
652e6a781dSBruce Richardson if (ret > 0 && ret < len) {
662e6a781dSBruce Richardson strcpy(buf, newbuf);
672e6a781dSBruce Richardson free(newbuf);
6899a2dd95SBruce Richardson return ret;
6999a2dd95SBruce Richardson }
702e6a781dSBruce Richardson free(newbuf);
7199a2dd95SBruce Richardson return 0; /* nothing written or modified */
7299a2dd95SBruce Richardson }
7399a2dd95SBruce Richardson
74babc5214SBruce Richardson static const char control_chars[0x20] = {
75babc5214SBruce Richardson ['\n'] = 'n',
76babc5214SBruce Richardson ['\r'] = 'r',
77babc5214SBruce Richardson ['\t'] = 't',
78babc5214SBruce Richardson };
79babc5214SBruce Richardson
80babc5214SBruce Richardson /**
81babc5214SBruce Richardson * @internal
82d23f5eabSBruce Richardson * Function that does the actual printing, used by __json_format_str. Modifies buffer
83d23f5eabSBruce Richardson * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
84babc5214SBruce Richardson */
85babc5214SBruce Richardson static inline int
__json_format_str_to_buf(char * buf,const int len,const char * prefix,const char * str,const char * suffix)860737585eSBruce Richardson __json_format_str_to_buf(char *buf, const int len,
87d23f5eabSBruce Richardson const char *prefix, const char *str, const char *suffix)
88babc5214SBruce Richardson {
890737585eSBruce Richardson int bufidx = 0;
90babc5214SBruce Richardson
910737585eSBruce Richardson while (*prefix != '\0' && bufidx < len)
920737585eSBruce Richardson buf[bufidx++] = *prefix++;
930737585eSBruce Richardson if (bufidx >= len)
9414d84fccSBruce Richardson return 0;
9514d84fccSBruce Richardson
96babc5214SBruce Richardson while (*str != '\0') {
97babc5214SBruce Richardson if (*str < (int)RTE_DIM(control_chars)) {
98babc5214SBruce Richardson int idx = *str; /* compilers don't like char type as index */
99babc5214SBruce Richardson if (control_chars[idx] != 0) {
1000737585eSBruce Richardson buf[bufidx++] = '\\';
1010737585eSBruce Richardson buf[bufidx++] = control_chars[idx];
102babc5214SBruce Richardson }
103babc5214SBruce Richardson } else if (*str == '"' || *str == '\\') {
1040737585eSBruce Richardson buf[bufidx++] = '\\';
1050737585eSBruce Richardson buf[bufidx++] = *str;
106babc5214SBruce Richardson } else
1070737585eSBruce Richardson buf[bufidx++] = *str;
10814d84fccSBruce Richardson /* we always need space for (at minimum) closing quote and null character.
109babc5214SBruce Richardson * Ensuring at least two free characters also means we can always take an
110babc5214SBruce Richardson * escaped character like "\n" without overflowing
111babc5214SBruce Richardson */
1120737585eSBruce Richardson if (bufidx > len - 2)
113babc5214SBruce Richardson return 0;
114babc5214SBruce Richardson str++;
115babc5214SBruce Richardson }
11614d84fccSBruce Richardson
1170737585eSBruce Richardson while (*suffix != '\0' && bufidx < len)
1180737585eSBruce Richardson buf[bufidx++] = *suffix++;
1190737585eSBruce Richardson if (bufidx >= len)
12014d84fccSBruce Richardson return 0;
12114d84fccSBruce Richardson
1220737585eSBruce Richardson buf[bufidx] = '\0';
1230737585eSBruce Richardson return bufidx;
124babc5214SBruce Richardson }
125babc5214SBruce Richardson
126d23f5eabSBruce Richardson /**
127d23f5eabSBruce Richardson * @internal
128d23f5eabSBruce Richardson * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
129d23f5eabSBruce Richardson * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
130d23f5eabSBruce Richardson * time constants, or values not needing escaping.
131d23f5eabSBruce Richardson * Drops any invalid characters we don't support
132d23f5eabSBruce Richardson */
133d23f5eabSBruce Richardson static inline int
__json_format_str(char * buf,const int len,const char * prefix,const char * str,const char * suffix)134d23f5eabSBruce Richardson __json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
135d23f5eabSBruce Richardson {
136d23f5eabSBruce Richardson int ret;
137a515b720SBruce Richardson char saved[4] = "";
138a515b720SBruce Richardson char *tmp;
139a515b720SBruce Richardson
140a515b720SBruce Richardson if (strnlen(buf, sizeof(saved)) < sizeof(saved)) {
141a515b720SBruce Richardson /* we have only a few bytes in buffer, so save them off to restore on error*/
142a515b720SBruce Richardson strcpy(saved, buf);
143a515b720SBruce Richardson ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
144a515b720SBruce Richardson if (ret == 0)
145a515b720SBruce Richardson strcpy(buf, saved); /* restore */
146a515b720SBruce Richardson return ret;
147a515b720SBruce Richardson }
148a515b720SBruce Richardson
149a515b720SBruce Richardson tmp = malloc(len);
150a515b720SBruce Richardson if (tmp == NULL)
151a515b720SBruce Richardson return 0;
152d23f5eabSBruce Richardson
153d23f5eabSBruce Richardson ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
154d23f5eabSBruce Richardson if (ret > 0)
155c9df59bcSDavid Marchand strcpy(buf, tmp);
156d23f5eabSBruce Richardson
157a515b720SBruce Richardson free(tmp);
158d23f5eabSBruce Richardson return ret;
159d23f5eabSBruce Richardson }
160d23f5eabSBruce Richardson
16199a2dd95SBruce Richardson /* Copies an empty array into the provided buffer. */
16299a2dd95SBruce Richardson static inline int
rte_tel_json_empty_array(char * buf,const int len,const int used)16399a2dd95SBruce Richardson rte_tel_json_empty_array(char *buf, const int len, const int used)
16499a2dd95SBruce Richardson {
16599a2dd95SBruce Richardson return used + __json_snprintf(buf + used, len - used, "[]");
16699a2dd95SBruce Richardson }
16799a2dd95SBruce Richardson
16899a2dd95SBruce Richardson /* Copies an empty object into the provided buffer. */
16999a2dd95SBruce Richardson static inline int
rte_tel_json_empty_obj(char * buf,const int len,const int used)17099a2dd95SBruce Richardson rte_tel_json_empty_obj(char *buf, const int len, const int used)
17199a2dd95SBruce Richardson {
17299a2dd95SBruce Richardson return used + __json_snprintf(buf + used, len - used, "{}");
17399a2dd95SBruce Richardson }
17499a2dd95SBruce Richardson
17599a2dd95SBruce Richardson /* Copies a string into the provided buffer, in JSON format. */
17699a2dd95SBruce Richardson static inline int
rte_tel_json_str(char * buf,const int len,const int used,const char * str)17799a2dd95SBruce Richardson rte_tel_json_str(char *buf, const int len, const int used, const char *str)
17899a2dd95SBruce Richardson {
17914d84fccSBruce Richardson return used + __json_format_str(buf + used, len - used, "\"", str, "\"");
18099a2dd95SBruce Richardson }
18199a2dd95SBruce Richardson
18299a2dd95SBruce Richardson /* Appends a string into the JSON array in the provided buffer. */
18399a2dd95SBruce Richardson static inline int
rte_tel_json_add_array_string(char * buf,const int len,const int used,const char * str)18499a2dd95SBruce Richardson rte_tel_json_add_array_string(char *buf, const int len, const int used,
18599a2dd95SBruce Richardson const char *str)
18699a2dd95SBruce Richardson {
18799a2dd95SBruce Richardson int ret, end = used - 1; /* strip off final delimiter */
18899a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '[]' */
18914d84fccSBruce Richardson return __json_format_str(buf, len, "[\"", str, "\"]");
19099a2dd95SBruce Richardson
19114d84fccSBruce Richardson ret = __json_format_str(buf + end, len - end, ",\"", str, "\"]");
19299a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
19399a2dd95SBruce Richardson }
19499a2dd95SBruce Richardson
19599a2dd95SBruce Richardson /* Appends an integer into the JSON array in the provided buffer. */
19699a2dd95SBruce Richardson static inline int
rte_tel_json_add_array_int(char * buf,const int len,const int used,int64_t val)1972f4520cdSBruce Richardson rte_tel_json_add_array_int(char *buf, const int len, const int used, int64_t val)
19899a2dd95SBruce Richardson {
19999a2dd95SBruce Richardson int ret, end = used - 1; /* strip off final delimiter */
20099a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '[]' */
2012f4520cdSBruce Richardson return __json_snprintf(buf, len, "[%"PRId64"]", val);
20299a2dd95SBruce Richardson
2032f4520cdSBruce Richardson ret = __json_snprintf(buf + end, len - end, ",%"PRId64"]", val);
20499a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
20599a2dd95SBruce Richardson }
20699a2dd95SBruce Richardson
20799a2dd95SBruce Richardson /* Appends a uint64_t into the JSON array in the provided buffer. */
20899a2dd95SBruce Richardson static inline int
rte_tel_json_add_array_uint(char * buf,const int len,const int used,uint64_t val)2092f4520cdSBruce Richardson rte_tel_json_add_array_uint(char *buf, const int len, const int used,
21099a2dd95SBruce Richardson uint64_t val)
21199a2dd95SBruce Richardson {
21299a2dd95SBruce Richardson int ret, end = used - 1; /* strip off final delimiter */
21399a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '[]' */
21499a2dd95SBruce Richardson return __json_snprintf(buf, len, "[%"PRIu64"]", val);
21599a2dd95SBruce Richardson
21699a2dd95SBruce Richardson ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val);
21799a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
21899a2dd95SBruce Richardson }
21999a2dd95SBruce Richardson
22099a2dd95SBruce Richardson /*
22199a2dd95SBruce Richardson * Add a new element with raw JSON value to the JSON array stored in the
22299a2dd95SBruce Richardson * provided buffer.
22399a2dd95SBruce Richardson */
22499a2dd95SBruce Richardson static inline int
rte_tel_json_add_array_json(char * buf,const int len,const int used,const char * str)22599a2dd95SBruce Richardson rte_tel_json_add_array_json(char *buf, const int len, const int used,
22699a2dd95SBruce Richardson const char *str)
22799a2dd95SBruce Richardson {
22899a2dd95SBruce Richardson int ret, end = used - 1; /* strip off final delimiter */
22999a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '[]' */
23099a2dd95SBruce Richardson return __json_snprintf(buf, len, "[%s]", str);
23199a2dd95SBruce Richardson
23299a2dd95SBruce Richardson ret = __json_snprintf(buf + end, len - end, ",%s]", str);
23399a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
23499a2dd95SBruce Richardson }
23599a2dd95SBruce Richardson
23699a2dd95SBruce Richardson /**
23799a2dd95SBruce Richardson * Add a new element with uint64_t value to the JSON object stored in the
23899a2dd95SBruce Richardson * provided buffer.
23999a2dd95SBruce Richardson */
24099a2dd95SBruce Richardson static inline int
rte_tel_json_add_obj_uint(char * buf,const int len,const int used,const char * name,uint64_t val)2412f4520cdSBruce Richardson rte_tel_json_add_obj_uint(char *buf, const int len, const int used,
24299a2dd95SBruce Richardson const char *name, uint64_t val)
24399a2dd95SBruce Richardson {
24499a2dd95SBruce Richardson int ret, end = used - 1;
24599a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '{}' */
24699a2dd95SBruce Richardson return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name,
24799a2dd95SBruce Richardson val);
24899a2dd95SBruce Richardson
24999a2dd95SBruce Richardson ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}",
25099a2dd95SBruce Richardson name, val);
25199a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
25299a2dd95SBruce Richardson }
25399a2dd95SBruce Richardson
25499a2dd95SBruce Richardson /**
25599a2dd95SBruce Richardson * Add a new element with int value to the JSON object stored in the
25699a2dd95SBruce Richardson * provided buffer.
25799a2dd95SBruce Richardson */
25899a2dd95SBruce Richardson static inline int
rte_tel_json_add_obj_int(char * buf,const int len,const int used,const char * name,int64_t val)25999a2dd95SBruce Richardson rte_tel_json_add_obj_int(char *buf, const int len, const int used,
2602f4520cdSBruce Richardson const char *name, int64_t val)
26199a2dd95SBruce Richardson {
26299a2dd95SBruce Richardson int ret, end = used - 1;
26399a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '{}' */
2642f4520cdSBruce Richardson return __json_snprintf(buf, len, "{\"%s\":%"PRId64"}", name,
26599a2dd95SBruce Richardson val);
26699a2dd95SBruce Richardson
2672f4520cdSBruce Richardson ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRId64"}",
26899a2dd95SBruce Richardson name, val);
26999a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
27099a2dd95SBruce Richardson }
27199a2dd95SBruce Richardson
27299a2dd95SBruce Richardson /**
27399a2dd95SBruce Richardson * Add a new element with string value to the JSON object stored in the
27499a2dd95SBruce Richardson * provided buffer.
27599a2dd95SBruce Richardson */
27699a2dd95SBruce Richardson static inline int
rte_tel_json_add_obj_str(char * buf,const int len,const int used,const char * name,const char * val)27799a2dd95SBruce Richardson rte_tel_json_add_obj_str(char *buf, const int len, const int used,
27899a2dd95SBruce Richardson const char *name, const char *val)
27999a2dd95SBruce Richardson {
2806918ef8eSBruce Richardson char tmp_name[RTE_TEL_MAX_STRING_LEN + 5];
28199a2dd95SBruce Richardson int ret, end = used - 1;
28299a2dd95SBruce Richardson
2836918ef8eSBruce Richardson /* names are limited to certain characters so need no escaping */
2846918ef8eSBruce Richardson snprintf(tmp_name, sizeof(tmp_name), "{\"%s\":\"", name);
2856918ef8eSBruce Richardson if (used <= 2) /* assume empty, since minimum is '{}' */
2866918ef8eSBruce Richardson return __json_format_str(buf, len, tmp_name, val, "\"}");
2876918ef8eSBruce Richardson
2886918ef8eSBruce Richardson tmp_name[0] = ','; /* replace '{' with ',' at start */
2896918ef8eSBruce Richardson ret = __json_format_str(buf + end, len - end, tmp_name, val, "\"}");
29099a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
29199a2dd95SBruce Richardson }
29299a2dd95SBruce Richardson
29399a2dd95SBruce Richardson /**
29499a2dd95SBruce Richardson * Add a new element with raw JSON value to the JSON object stored in the
29599a2dd95SBruce Richardson * provided buffer.
29699a2dd95SBruce Richardson */
29799a2dd95SBruce Richardson static inline int
rte_tel_json_add_obj_json(char * buf,const int len,const int used,const char * name,const char * val)29899a2dd95SBruce Richardson rte_tel_json_add_obj_json(char *buf, const int len, const int used,
29999a2dd95SBruce Richardson const char *name, const char *val)
30099a2dd95SBruce Richardson {
30199a2dd95SBruce Richardson int ret, end = used - 1;
30299a2dd95SBruce Richardson if (used <= 2) /* assume empty, since minimum is '{}' */
30399a2dd95SBruce Richardson return __json_snprintf(buf, len, "{\"%s\":%s}", name, val);
30499a2dd95SBruce Richardson
30599a2dd95SBruce Richardson ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}",
30699a2dd95SBruce Richardson name, val);
30799a2dd95SBruce Richardson return ret == 0 ? used : end + ret;
30899a2dd95SBruce Richardson }
30999a2dd95SBruce Richardson
31099a2dd95SBruce Richardson #endif /*_RTE_TELEMETRY_JSON_H_*/
311