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 __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 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 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 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 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 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 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 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 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 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 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 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 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 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