xref: /dpdk/lib/telemetry/telemetry_json.h (revision 3e4c5be9f9ddfb3d899053a615ee4d293e449d4d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #ifndef _RTE_TELEMETRY_JSON_H_
6 #define _RTE_TELEMETRY_JSON_H_
7 
8 #include <inttypes.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <rte_common.h>
13 #include <rte_telemetry.h>
14 
15 /**
16  * @file
17  * Internal Telemetry Utility functions
18  *
19  * This file contains small inline functions to make it easier for applications
20  * to build up valid JSON responses to telemetry requests.
21  */
22 
23 /**
24  * @internal
25  * Copies a value into a buffer if the buffer has enough available space.
26  * Nothing written to buffer if an overflow occurs.
27  * This function is not for use for values larger than given buffer length.
28  */
29 __rte_format_printf(3, 4)
30 static inline int
__json_snprintf(char * buf,const int len,const char * format,...)31 __json_snprintf(char *buf, const int len, const char *format, ...)
32 {
33 	va_list ap;
34 	char tmp[4];
35 	char *newbuf;
36 	int ret;
37 
38 	if (len == 0)
39 		return 0;
40 
41 	/* to ensure unmodified if we overflow, we save off any values currently in buf
42 	 * before we printf, if they are short enough. We restore them on error.
43 	 */
44 	if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
45 		strcpy(tmp, buf);  /* strcpy is safe as we know the length */
46 		va_start(ap, format);
47 		ret = vsnprintf(buf, len, format, ap);
48 		va_end(ap);
49 		if (ret > 0 && ret < len)
50 			return ret;
51 		strcpy(buf, tmp);  /* restore on error */
52 		return 0;
53 	}
54 
55 	/* in normal operations should never hit this, but can do if buffer is
56 	 * incorrectly initialized e.g. in unit test cases
57 	 */
58 	newbuf = malloc(len);
59 	if (newbuf == NULL)
60 		return 0;
61 
62 	va_start(ap, format);
63 	ret = vsnprintf(newbuf, len, format, ap);
64 	va_end(ap);
65 	if (ret > 0 && ret < len) {
66 		strcpy(buf, newbuf);
67 		free(newbuf);
68 		return ret;
69 	}
70 	free(newbuf);
71 	return 0; /* nothing written or modified */
72 }
73 
74 static const char control_chars[0x20] = {
75 		['\n'] = 'n',
76 		['\r'] = 'r',
77 		['\t'] = 't',
78 };
79 
80 /**
81  * @internal
82  * Function that does the actual printing, used by __json_format_str. Modifies buffer
83  * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
84  */
85 static inline int
__json_format_str_to_buf(char * buf,const int len,const char * prefix,const char * str,const char * suffix)86 __json_format_str_to_buf(char *buf, const int len,
87 		const char *prefix, const char *str, const char *suffix)
88 {
89 	int bufidx = 0;
90 
91 	while (*prefix != '\0' && bufidx < len)
92 		buf[bufidx++] = *prefix++;
93 	if (bufidx >= len)
94 		return 0;
95 
96 	while (*str != '\0') {
97 		if (*str < (int)RTE_DIM(control_chars)) {
98 			int idx = *str;  /* compilers don't like char type as index */
99 			if (control_chars[idx] != 0) {
100 				buf[bufidx++] = '\\';
101 				buf[bufidx++] = control_chars[idx];
102 			}
103 		} else if (*str == '"' || *str == '\\') {
104 			buf[bufidx++] = '\\';
105 			buf[bufidx++] = *str;
106 		} else
107 			buf[bufidx++] = *str;
108 		/* we always need space for (at minimum) closing quote and null character.
109 		 * Ensuring at least two free characters also means we can always take an
110 		 * escaped character like "\n" without overflowing
111 		 */
112 		if (bufidx > len - 2)
113 			return 0;
114 		str++;
115 	}
116 
117 	while (*suffix != '\0' && bufidx < len)
118 		buf[bufidx++] = *suffix++;
119 	if (bufidx >= len)
120 		return 0;
121 
122 	buf[bufidx] = '\0';
123 	return bufidx;
124 }
125 
126 /**
127  * @internal
128  * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
129  * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
130  * time constants, or values not needing escaping.
131  * Drops any invalid characters we don't support
132  */
133 static inline int
__json_format_str(char * buf,const int len,const char * prefix,const char * str,const char * suffix)134 __json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
135 {
136 	int ret;
137 	char saved[4] = "";
138 	char *tmp;
139 
140 	if (strnlen(buf, sizeof(saved)) < sizeof(saved)) {
141 		/* we have only a few bytes in buffer, so save them off to restore on error*/
142 		strcpy(saved, buf);
143 		ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
144 		if (ret == 0)
145 			strcpy(buf, saved); /* restore */
146 		return ret;
147 	}
148 
149 	tmp = malloc(len);
150 	if (tmp == NULL)
151 		return 0;
152 
153 	ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
154 	if (ret > 0)
155 		strcpy(buf, tmp);
156 
157 	free(tmp);
158 	return ret;
159 }
160 
161 /* Copies an empty array into the provided buffer. */
162 static inline int
rte_tel_json_empty_array(char * buf,const int len,const int used)163 rte_tel_json_empty_array(char *buf, const int len, const int used)
164 {
165 	return used + __json_snprintf(buf + used, len - used, "[]");
166 }
167 
168 /* Copies an empty object into the provided buffer. */
169 static inline int
rte_tel_json_empty_obj(char * buf,const int len,const int used)170 rte_tel_json_empty_obj(char *buf, const int len, const int used)
171 {
172 	return used + __json_snprintf(buf + used, len - used, "{}");
173 }
174 
175 /* Copies a string into the provided buffer, in JSON format. */
176 static inline int
rte_tel_json_str(char * buf,const int len,const int used,const char * str)177 rte_tel_json_str(char *buf, const int len, const int used, const char *str)
178 {
179 	return used + __json_format_str(buf + used, len - used, "\"", str, "\"");
180 }
181 
182 /* Appends a string into the JSON array in the provided buffer. */
183 static inline int
rte_tel_json_add_array_string(char * buf,const int len,const int used,const char * str)184 rte_tel_json_add_array_string(char *buf, const int len, const int used,
185 		const char *str)
186 {
187 	int ret, end = used - 1; /* strip off final delimiter */
188 	if (used <= 2) /* assume empty, since minimum is '[]' */
189 		return __json_format_str(buf, len, "[\"", str, "\"]");
190 
191 	ret = __json_format_str(buf + end, len - end, ",\"", str, "\"]");
192 	return ret == 0 ? used : end + ret;
193 }
194 
195 /* Appends an integer into the JSON array in the provided buffer. */
196 static inline int
rte_tel_json_add_array_int(char * buf,const int len,const int used,int64_t val)197 rte_tel_json_add_array_int(char *buf, const int len, const int used, int64_t val)
198 {
199 	int ret, end = used - 1; /* strip off final delimiter */
200 	if (used <= 2) /* assume empty, since minimum is '[]' */
201 		return __json_snprintf(buf, len, "[%"PRId64"]", val);
202 
203 	ret = __json_snprintf(buf + end, len - end, ",%"PRId64"]", val);
204 	return ret == 0 ? used : end + ret;
205 }
206 
207 /* Appends a uint64_t into the JSON array in the provided buffer. */
208 static inline int
rte_tel_json_add_array_uint(char * buf,const int len,const int used,uint64_t val)209 rte_tel_json_add_array_uint(char *buf, const int len, const int used,
210 		uint64_t val)
211 {
212 	int ret, end = used - 1; /* strip off final delimiter */
213 	if (used <= 2) /* assume empty, since minimum is '[]' */
214 		return __json_snprintf(buf, len, "[%"PRIu64"]", val);
215 
216 	ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val);
217 	return ret == 0 ? used : end + ret;
218 }
219 
220 /*
221  * Add a new element with raw JSON value to the JSON array stored in the
222  * provided buffer.
223  */
224 static inline int
rte_tel_json_add_array_json(char * buf,const int len,const int used,const char * str)225 rte_tel_json_add_array_json(char *buf, const int len, const int used,
226 		const char *str)
227 {
228 	int ret, end = used - 1; /* strip off final delimiter */
229 	if (used <= 2) /* assume empty, since minimum is '[]' */
230 		return __json_snprintf(buf, len, "[%s]", str);
231 
232 	ret = __json_snprintf(buf + end, len - end, ",%s]", str);
233 	return ret == 0 ? used : end + ret;
234 }
235 
236 /**
237  * Add a new element with uint64_t value to the JSON object stored in the
238  * provided buffer.
239  */
240 static inline int
rte_tel_json_add_obj_uint(char * buf,const int len,const int used,const char * name,uint64_t val)241 rte_tel_json_add_obj_uint(char *buf, const int len, const int used,
242 		const char *name, uint64_t val)
243 {
244 	int ret, end = used - 1;
245 	if (used <= 2) /* assume empty, since minimum is '{}' */
246 		return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name,
247 				val);
248 
249 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}",
250 			name, val);
251 	return ret == 0 ? used : end + ret;
252 }
253 
254 /**
255  * Add a new element with int value to the JSON object stored in the
256  * provided buffer.
257  */
258 static inline int
rte_tel_json_add_obj_int(char * buf,const int len,const int used,const char * name,int64_t val)259 rte_tel_json_add_obj_int(char *buf, const int len, const int used,
260 		const char *name, int64_t val)
261 {
262 	int ret, end = used - 1;
263 	if (used <= 2) /* assume empty, since minimum is '{}' */
264 		return __json_snprintf(buf, len, "{\"%s\":%"PRId64"}", name,
265 				val);
266 
267 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRId64"}",
268 			name, val);
269 	return ret == 0 ? used : end + ret;
270 }
271 
272 /**
273  * Add a new element with string value to the JSON object stored in the
274  * provided buffer.
275  */
276 static inline int
rte_tel_json_add_obj_str(char * buf,const int len,const int used,const char * name,const char * val)277 rte_tel_json_add_obj_str(char *buf, const int len, const int used,
278 		const char *name, const char *val)
279 {
280 	char tmp_name[RTE_TEL_MAX_STRING_LEN + 5];
281 	int ret, end = used - 1;
282 
283 	/* names are limited to certain characters so need no escaping */
284 	snprintf(tmp_name, sizeof(tmp_name), "{\"%s\":\"", name);
285 	if (used <= 2) /* assume empty, since minimum is '{}' */
286 		return __json_format_str(buf, len, tmp_name, val, "\"}");
287 
288 	tmp_name[0] = ',';  /* replace '{' with ',' at start */
289 	ret = __json_format_str(buf + end, len - end, tmp_name, val, "\"}");
290 	return ret == 0 ? used : end + ret;
291 }
292 
293 /**
294  * Add a new element with raw JSON value to the JSON object stored in the
295  * provided buffer.
296  */
297 static inline int
rte_tel_json_add_obj_json(char * buf,const int len,const int used,const char * name,const char * val)298 rte_tel_json_add_obj_json(char *buf, const int len, const int used,
299 		const char *name, const char *val)
300 {
301 	int ret, end = used - 1;
302 	if (used <= 2) /* assume empty, since minimum is '{}' */
303 		return __json_snprintf(buf, len, "{\"%s\":%s}", name, val);
304 
305 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}",
306 			name, val);
307 	return ret == 0 ? used : end + ret;
308 }
309 
310 #endif /*_RTE_TELEMETRY_JSON_H_*/
311