xref: /dpdk/lib/telemetry/telemetry_json.h (revision 3e4c5be9f9ddfb3d899053a615ee4d293e449d4d)
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