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