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