xref: /dpdk/lib/telemetry/telemetry_json.h (revision 33b84a2efca7ac188def108ba8b981daa7572b9a)
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 <rte_common.h>
12 #include <rte_telemetry.h>
13 
14 /**
15  * @file
16  * Internal Telemetry Utility functions
17  *
18  * This file contains small inline functions to make it easier for applications
19  * to build up valid JSON responses to telemetry requests.
20  *
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
31 __json_snprintf(char *buf, const int len, const char *format, ...)
32 {
33 	char tmp[len];
34 	va_list ap;
35 	int ret;
36 
37 	va_start(ap, format);
38 	ret = vsnprintf(tmp, sizeof(tmp), format, ap);
39 	va_end(ap);
40 	if (ret > 0 && ret < (int)sizeof(tmp) && ret < len) {
41 		strcpy(buf, tmp);
42 		return ret;
43 	}
44 	return 0; /* nothing written or modified */
45 }
46 
47 static const char control_chars[0x20] = {
48 		['\n'] = 'n',
49 		['\r'] = 'r',
50 		['\t'] = 't',
51 };
52 
53 /**
54  * @internal
55  * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
56  * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
57  * time constants, or values not needing escaping.
58  * Drops any invalid characters we don't support
59  */
60 static inline int
61 __json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
62 {
63 	char tmp[len];
64 	int tmpidx = 0;
65 
66 	while (*prefix != '\0' && tmpidx < len)
67 		tmp[tmpidx++] = *prefix++;
68 	if (tmpidx >= len)
69 		return 0;
70 
71 	while (*str != '\0') {
72 		if (*str < (int)RTE_DIM(control_chars)) {
73 			int idx = *str;  /* compilers don't like char type as index */
74 			if (control_chars[idx] != 0) {
75 				tmp[tmpidx++] = '\\';
76 				tmp[tmpidx++] = control_chars[idx];
77 			}
78 		} else if (*str == '"' || *str == '\\') {
79 			tmp[tmpidx++] = '\\';
80 			tmp[tmpidx++] = *str;
81 		} else
82 			tmp[tmpidx++] = *str;
83 		/* we always need space for (at minimum) closing quote and null character.
84 		 * Ensuring at least two free characters also means we can always take an
85 		 * escaped character like "\n" without overflowing
86 		 */
87 		if (tmpidx > len - 2)
88 			return 0;
89 		str++;
90 	}
91 
92 	while (*suffix != '\0' && tmpidx < len)
93 		tmp[tmpidx++] = *suffix++;
94 	if (tmpidx >= len)
95 		return 0;
96 
97 	tmp[tmpidx] = '\0';
98 
99 	strcpy(buf, tmp);
100 	return tmpidx;
101 }
102 
103 /* Copies an empty array into the provided buffer. */
104 static inline int
105 rte_tel_json_empty_array(char *buf, const int len, const int used)
106 {
107 	return used + __json_snprintf(buf + used, len - used, "[]");
108 }
109 
110 /* Copies an empty object into the provided buffer. */
111 static inline int
112 rte_tel_json_empty_obj(char *buf, const int len, const int used)
113 {
114 	return used + __json_snprintf(buf + used, len - used, "{}");
115 }
116 
117 /* Copies a string into the provided buffer, in JSON format. */
118 static inline int
119 rte_tel_json_str(char *buf, const int len, const int used, const char *str)
120 {
121 	return used + __json_format_str(buf + used, len - used, "\"", str, "\"");
122 }
123 
124 /* Appends a string into the JSON array in the provided buffer. */
125 static inline int
126 rte_tel_json_add_array_string(char *buf, const int len, const int used,
127 		const char *str)
128 {
129 	int ret, end = used - 1; /* strip off final delimiter */
130 	if (used <= 2) /* assume empty, since minimum is '[]' */
131 		return __json_format_str(buf, len, "[\"", str, "\"]");
132 
133 	ret = __json_format_str(buf + end, len - end, ",\"", str, "\"]");
134 	return ret == 0 ? used : end + ret;
135 }
136 
137 /* Appends an integer into the JSON array in the provided buffer. */
138 static inline int
139 rte_tel_json_add_array_int(char *buf, const int len, const int used, int64_t val)
140 {
141 	int ret, end = used - 1; /* strip off final delimiter */
142 	if (used <= 2) /* assume empty, since minimum is '[]' */
143 		return __json_snprintf(buf, len, "[%"PRId64"]", val);
144 
145 	ret = __json_snprintf(buf + end, len - end, ",%"PRId64"]", val);
146 	return ret == 0 ? used : end + ret;
147 }
148 
149 /* Appends a uint64_t into the JSON array in the provided buffer. */
150 static inline int
151 rte_tel_json_add_array_uint(char *buf, const int len, const int used,
152 		uint64_t val)
153 {
154 	int ret, end = used - 1; /* strip off final delimiter */
155 	if (used <= 2) /* assume empty, since minimum is '[]' */
156 		return __json_snprintf(buf, len, "[%"PRIu64"]", val);
157 
158 	ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val);
159 	return ret == 0 ? used : end + ret;
160 }
161 
162 /*
163  * Add a new element with raw JSON value to the JSON array stored in the
164  * provided buffer.
165  */
166 static inline int
167 rte_tel_json_add_array_json(char *buf, const int len, const int used,
168 		const char *str)
169 {
170 	int ret, end = used - 1; /* strip off final delimiter */
171 	if (used <= 2) /* assume empty, since minimum is '[]' */
172 		return __json_snprintf(buf, len, "[%s]", str);
173 
174 	ret = __json_snprintf(buf + end, len - end, ",%s]", str);
175 	return ret == 0 ? used : end + ret;
176 }
177 
178 /**
179  * Add a new element with uint64_t value to the JSON object stored in the
180  * provided buffer.
181  */
182 static inline int
183 rte_tel_json_add_obj_uint(char *buf, const int len, const int used,
184 		const char *name, uint64_t val)
185 {
186 	int ret, end = used - 1;
187 	if (used <= 2) /* assume empty, since minimum is '{}' */
188 		return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name,
189 				val);
190 
191 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}",
192 			name, val);
193 	return ret == 0 ? used : end + ret;
194 }
195 
196 /**
197  * Add a new element with int value to the JSON object stored in the
198  * provided buffer.
199  */
200 static inline int
201 rte_tel_json_add_obj_int(char *buf, const int len, const int used,
202 		const char *name, int64_t val)
203 {
204 	int ret, end = used - 1;
205 	if (used <= 2) /* assume empty, since minimum is '{}' */
206 		return __json_snprintf(buf, len, "{\"%s\":%"PRId64"}", name,
207 				val);
208 
209 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRId64"}",
210 			name, val);
211 	return ret == 0 ? used : end + ret;
212 }
213 
214 /**
215  * Add a new element with string value to the JSON object stored in the
216  * provided buffer.
217  */
218 static inline int
219 rte_tel_json_add_obj_str(char *buf, const int len, const int used,
220 		const char *name, const char *val)
221 {
222 	char tmp_name[RTE_TEL_MAX_STRING_LEN + 5];
223 	int ret, end = used - 1;
224 
225 	/* names are limited to certain characters so need no escaping */
226 	snprintf(tmp_name, sizeof(tmp_name), "{\"%s\":\"", name);
227 	if (used <= 2) /* assume empty, since minimum is '{}' */
228 		return __json_format_str(buf, len, tmp_name, val, "\"}");
229 
230 	tmp_name[0] = ',';  /* replace '{' with ',' at start */
231 	ret = __json_format_str(buf + end, len - end, tmp_name, val, "\"}");
232 	return ret == 0 ? used : end + ret;
233 }
234 
235 /**
236  * Add a new element with raw JSON value to the JSON object stored in the
237  * provided buffer.
238  */
239 static inline int
240 rte_tel_json_add_obj_json(char *buf, const int len, const int used,
241 		const char *name, const char *val)
242 {
243 	int ret, end = used - 1;
244 	if (used <= 2) /* assume empty, since minimum is '{}' */
245 		return __json_snprintf(buf, len, "{\"%s\":%s}", name, val);
246 
247 	ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}",
248 			name, val);
249 	return ret == 0 ? used : end + ret;
250 }
251 
252 #endif /*_RTE_TELEMETRY_JSON_H_*/
253