Branch data Line data Source code
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
31 : 152 : __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 [ + - ]: 152 : 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 [ + + ]: 152 : if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
45 : : strcpy(tmp, buf); /* strcpy is safe as we know the length */
46 : 150 : va_start(ap, format);
47 [ + + ]: 150 : ret = vsnprintf(buf, len, format, ap);
48 : 150 : va_end(ap);
49 [ + + ]: 150 : if (ret > 0 && ret < len)
50 : : return ret;
51 : : strcpy(buf, tmp); /* restore on error */
52 : 5 : 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 : 2 : newbuf = malloc(len);
59 [ + - ]: 2 : if (newbuf == NULL)
60 : : return 0;
61 : :
62 [ + - ]: 2 : va_start(ap, format);
63 : : ret = vsnprintf(newbuf, len, format, ap);
64 : 2 : va_end(ap);
65 [ + - ]: 2 : if (ret > 0 && ret < len) {
66 : : strcpy(buf, newbuf);
67 : 2 : free(newbuf);
68 : 2 : return ret;
69 : : }
70 : 0 : free(newbuf);
71 : 0 : 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
86 : 56 : __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 [ + + + - ]: 300 : while (*prefix != '\0' && bufidx < len)
92 : 244 : buf[bufidx++] = *prefix++;
93 [ + - ]: 56 : if (bufidx >= len)
94 : : return 0;
95 : :
96 [ + + ]: 543 : while (*str != '\0') {
97 [ + + ]: 490 : if (*str < (int)RTE_DIM(control_chars)) {
98 : 13 : int idx = *str; /* compilers don't like char type as index */
99 [ + - ]: 13 : if (control_chars[idx] != 0) {
100 : 13 : buf[bufidx++] = '\\';
101 : 13 : buf[bufidx++] = control_chars[idx];
102 : : }
103 [ + + ]: 477 : } else if (*str == '"' || *str == '\\') {
104 : 7 : buf[bufidx++] = '\\';
105 : 7 : buf[bufidx++] = *str;
106 : : } else
107 : 470 : 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 [ + + ]: 490 : if (bufidx > len - 2)
113 : : return 0;
114 : 487 : str++;
115 : : }
116 : :
117 [ + + + - ]: 156 : while (*suffix != '\0' && bufidx < len)
118 : 103 : buf[bufidx++] = *suffix++;
119 [ + - ]: 53 : if (bufidx >= len)
120 : : return 0;
121 : :
122 : 53 : buf[bufidx] = '\0';
123 : 53 : 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
134 : 56 : __json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
135 : : {
136 : : int ret;
137 : 56 : char saved[4] = "";
138 : : char *tmp;
139 : :
140 [ + - ]: 56 : 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 : 56 : ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
144 [ + + ]: 56 : if (ret == 0)
145 : : strcpy(buf, saved); /* restore */
146 : 56 : return ret;
147 : : }
148 : :
149 : 0 : tmp = malloc(len);
150 [ # # ]: 0 : if (tmp == NULL)
151 : : return 0;
152 : :
153 : 0 : ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
154 [ # # ]: 0 : if (ret > 0)
155 : : strcpy(buf, tmp);
156 : :
157 : 0 : free(tmp);
158 : 0 : return ret;
159 : : }
160 : :
161 : : /* Copies an empty array into the provided buffer. */
162 : : static inline int
163 : : rte_tel_json_empty_array(char *buf, const int len, const int used)
164 : : {
165 : 35 : return used + __json_snprintf(buf + used, len - used, "[]");
166 : : }
167 : :
168 : : /* Copies an empty object into the provided buffer. */
169 : : static inline int
170 : : rte_tel_json_empty_obj(char *buf, const int len, const int used)
171 : : {
172 : 14 : 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
177 : : rte_tel_json_str(char *buf, const int len, const int used, const char *str)
178 : : {
179 : 3 : 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
184 : 37 : rte_tel_json_add_array_string(char *buf, const int len, const int used,
185 : : const char *str)
186 : : {
187 : 37 : int ret, end = used - 1; /* strip off final delimiter */
188 [ + + ]: 37 : if (used <= 2) /* assume empty, since minimum is '[]' */
189 : 22 : return __json_format_str(buf, len, "[\"", str, "\"]");
190 : :
191 : 15 : ret = __json_format_str(buf + end, len - end, ",\"", str, "\"]");
192 [ + + ]: 15 : return ret == 0 ? used : end + ret;
193 : : }
194 : :
195 : : /* Appends an integer into the JSON array in the provided buffer. */
196 : : static inline int
197 : 26 : rte_tel_json_add_array_int(char *buf, const int len, const int used, int64_t val)
198 : : {
199 : 26 : int ret, end = used - 1; /* strip off final delimiter */
200 [ + + ]: 26 : if (used <= 2) /* assume empty, since minimum is '[]' */
201 : 5 : return __json_snprintf(buf, len, "[%"PRId64"]", val);
202 : :
203 : 21 : ret = __json_snprintf(buf + end, len - end, ",%"PRId64"]", val);
204 [ + - ]: 21 : 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
209 : 35 : rte_tel_json_add_array_uint(char *buf, const int len, const int used,
210 : : uint64_t val)
211 : : {
212 : 35 : int ret, end = used - 1; /* strip off final delimiter */
213 [ + + ]: 35 : if (used <= 2) /* assume empty, since minimum is '[]' */
214 : 5 : return __json_snprintf(buf, len, "[%"PRIu64"]", val);
215 : :
216 : 30 : ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val);
217 [ + - ]: 30 : 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
225 : 10 : rte_tel_json_add_array_json(char *buf, const int len, const int used,
226 : : const char *str)
227 : : {
228 : 10 : int ret, end = used - 1; /* strip off final delimiter */
229 [ + + ]: 10 : if (used <= 2) /* assume empty, since minimum is '[]' */
230 : 5 : return __json_snprintf(buf, len, "[%s]", str);
231 : :
232 : 5 : ret = __json_snprintf(buf + end, len - end, ",%s]", str);
233 [ + - ]: 5 : 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
241 : 14 : rte_tel_json_add_obj_uint(char *buf, const int len, const int used,
242 : : const char *name, uint64_t val)
243 : : {
244 : 14 : int ret, end = used - 1;
245 [ + + ]: 14 : if (used <= 2) /* assume empty, since minimum is '{}' */
246 : 4 : return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name,
247 : : val);
248 : :
249 : 10 : ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}",
250 : : name, val);
251 [ + + ]: 10 : 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
259 : 5 : rte_tel_json_add_obj_int(char *buf, const int len, const int used,
260 : : const char *name, int64_t val)
261 : : {
262 : 5 : int ret, end = used - 1;
263 [ + + ]: 5 : if (used <= 2) /* assume empty, since minimum is '{}' */
264 : 1 : return __json_snprintf(buf, len, "{\"%s\":%"PRId64"}", name,
265 : : val);
266 : :
267 : 4 : ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRId64"}",
268 : : name, val);
269 [ + - ]: 4 : 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
277 : 16 : 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 [ + + ]: 16 : 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 [ + + ]: 16 : if (used <= 2) /* assume empty, since minimum is '{}' */
286 : 5 : return __json_format_str(buf, len, tmp_name, val, "\"}");
287 : :
288 : 11 : tmp_name[0] = ','; /* replace '{' with ',' at start */
289 : 11 : ret = __json_format_str(buf + end, len - end, tmp_name, val, "\"}");
290 [ + - ]: 11 : 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
298 : 13 : rte_tel_json_add_obj_json(char *buf, const int len, const int used,
299 : : const char *name, const char *val)
300 : : {
301 : 13 : int ret, end = used - 1;
302 [ + + ]: 13 : if (used <= 2) /* assume empty, since minimum is '{}' */
303 : 7 : return __json_snprintf(buf, len, "{\"%s\":%s}", name, val);
304 : :
305 : 6 : ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}",
306 : : name, val);
307 [ + - ]: 6 : return ret == 0 ? used : end + ret;
308 : : }
309 : :
310 : : #endif /*_RTE_TELEMETRY_JSON_H_*/
|