1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Intel Corporation 3 */ 4 #include <ctype.h> 5 #include <errno.h> 6 #ifndef RTE_EXEC_ENV_WINDOWS 7 #include <unistd.h> 8 #include <sys/socket.h> 9 #include <sys/un.h> 10 #include <pthread.h> 11 #endif /* !RTE_EXEC_ENV_WINDOWS */ 12 13 /* we won't link against libbsd, so just always use DPDKs-specific strlcpy */ 14 #undef RTE_USE_LIBBSD 15 #include <rte_string_fns.h> 16 #include <rte_common.h> 17 #include <rte_spinlock.h> 18 19 #include "telemetry_internal.h" 20 21 #define MAX_LEN 128 22 #define BUF_SIZE 1024 23 #define CLIENTS_UNREG_ACTION "\"action\":2" 24 #define CLIENTS_CMD "\"command\":\"clients\"" 25 #define CLIENTS_DATA "\"data\":{\"client_path\":\"" 26 #define STATS_ACTION "\"action\":0" 27 #define DATA_REQ_LABEL "\"data\":" 28 #define TELEMETRY_LEGACY_MAX_CALLBACKS 4 29 30 31 static int 32 register_client(const char *cmd __rte_unused, 33 const char *params __rte_unused, 34 char *buffer, int buf_len); 35 36 struct json_command { 37 char action[MAX_LEN]; 38 char cmd[MAX_LEN]; 39 char data[MAX_LEN]; 40 telemetry_legacy_cb fn; 41 42 }; 43 44 struct json_command callbacks[TELEMETRY_LEGACY_MAX_CALLBACKS] = { 45 { 46 .action = "\"action\":1", 47 .cmd = CLIENTS_CMD, 48 .data = CLIENTS_DATA, 49 .fn = register_client 50 } 51 }; 52 int num_legacy_callbacks = 1; 53 static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; 54 55 int 56 rte_telemetry_legacy_register(const char *cmd, 57 enum rte_telemetry_legacy_data_req data_req, 58 telemetry_legacy_cb fn) 59 { 60 if (fn == NULL) 61 return -EINVAL; 62 if (num_legacy_callbacks >= (int) RTE_DIM(callbacks)) 63 return -ENOENT; 64 65 rte_spinlock_lock(&callback_sl); 66 strlcpy(callbacks[num_legacy_callbacks].action, STATS_ACTION, MAX_LEN); 67 snprintf(callbacks[num_legacy_callbacks].cmd, MAX_LEN, 68 "\"command\":\"%s\"", cmd); 69 snprintf(callbacks[num_legacy_callbacks].data, MAX_LEN, 70 data_req ? "%s{\"" : "%snull", 71 DATA_REQ_LABEL); 72 callbacks[num_legacy_callbacks].fn = fn; 73 num_legacy_callbacks++; 74 rte_spinlock_unlock(&callback_sl); 75 76 return 0; 77 } 78 79 static int 80 register_client(const char *cmd __rte_unused, const char *params, 81 char *buffer __rte_unused, int buf_len __rte_unused) 82 { 83 #ifndef RTE_EXEC_ENV_WINDOWS 84 pthread_t th; 85 char data[BUF_SIZE]; 86 int fd; 87 int rc; 88 struct sockaddr_un addrs; 89 #endif /* !RTE_EXEC_ENV_WINDOWS */ 90 91 if (!strchr(params, ':')) { 92 fprintf(stderr, "Invalid data\n"); 93 return -1; 94 } 95 #ifndef RTE_EXEC_ENV_WINDOWS 96 strlcpy(data, strchr(params, ':'), sizeof(data)); 97 memcpy(data, &data[strlen(":\"")], strlen(data)); 98 if (!strchr(data, '\"')) { 99 fprintf(stderr, "Invalid client data\n"); 100 return -1; 101 } 102 *strchr(data, '\"') = 0; 103 104 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 105 if (fd < 0) { 106 perror("Failed to open socket"); 107 return -1; 108 } 109 addrs.sun_family = AF_UNIX; 110 strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path)); 111 112 if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) { 113 perror("\nClient connection error\n"); 114 close(fd); 115 return -1; 116 } 117 rc = pthread_create(&th, NULL, &legacy_client_handler, 118 (void *)(uintptr_t)fd); 119 if (rc != 0) { 120 fprintf(stderr, "Failed to create legacy client thread: %s\n", 121 strerror(rc)); 122 close(fd); 123 return -1; 124 } 125 pthread_detach(th); 126 #endif /* !RTE_EXEC_ENV_WINDOWS */ 127 return 0; 128 } 129 130 #ifndef RTE_EXEC_ENV_WINDOWS 131 132 static int 133 send_error_response(int s, int err) 134 { 135 const char *desc; 136 char out_buf[100000]; 137 138 switch (err) { 139 case -ENOMEM: 140 desc = "Memory Allocation Error"; 141 break; 142 case -EINVAL: 143 desc = "Invalid Argument 404"; 144 break; 145 case -EPERM: 146 desc = "Unknown"; 147 break; 148 default: 149 /* Default case keeps behaviour of Telemetry library */ 150 printf("\nInvalid error type: %d\n", err); 151 return -EINVAL; 152 } 153 int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": " 154 "\"Status Error: %s\", \"data\": null}", desc); 155 if (write(s, out_buf, used) < 0) { 156 perror("Error writing to socket"); 157 return -1; 158 } 159 return 0; 160 } 161 162 static void 163 perform_command(telemetry_legacy_cb fn, const char *param, int s) 164 { 165 char out_buf[100000]; 166 int ret, used = 0; 167 168 ret = fn("", param, out_buf, sizeof(out_buf)); 169 if (ret < 0) { 170 ret = send_error_response(s, ret); 171 if (ret < 0) 172 printf("\nCould not send error response\n"); 173 return; 174 } 175 used += ret; 176 if (write(s, out_buf, used) < 0) 177 perror("Error writing to socket"); 178 } 179 180 static int 181 parse_client_request(char *buffer, int buf_len, int s) 182 { 183 int i; 184 char *data = buffer + buf_len; 185 telemetry_legacy_cb fn = NULL; 186 const char *valid_sep = ",}"; 187 if (buffer[0] != '{' || buffer[buf_len - 1] != '}') 188 return -EPERM; 189 190 if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD) 191 && strstr(buffer, CLIENTS_DATA)) 192 return 0; 193 194 for (i = 0; i < num_legacy_callbacks; i++) { 195 char *action_ptr = strstr(buffer, callbacks[i].action); 196 char *cmd_ptr = strstr(buffer, callbacks[i].cmd); 197 char *data_ptr = strstr(buffer, callbacks[i].data); 198 if (!action_ptr || !cmd_ptr || !data_ptr) 199 continue; 200 201 char action_sep = action_ptr[strlen(callbacks[i].action)]; 202 char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)]; 203 if (!(strchr(valid_sep, action_sep) && strchr(valid_sep, 204 cmd_sep))) 205 return -EPERM; 206 char data_sep; 207 208 if (!strchr(data_ptr, '{')) 209 data_sep = data_ptr[strlen(callbacks[i].data)]; 210 else { 211 if (!strchr(data_ptr, '}')) 212 return -EINVAL; 213 char *data_end = strchr(data_ptr, '}'); 214 data = data_ptr + strlen(DATA_REQ_LABEL); 215 data_sep = data_end[1]; 216 data_end[1] = 0; 217 } 218 if (!strchr(valid_sep, data_sep)) 219 return -EPERM; 220 fn = callbacks[i].fn; 221 break; 222 } 223 224 if (!fn) 225 return -EINVAL; 226 perform_command(fn, data, s); 227 return 0; 228 } 229 230 void * 231 legacy_client_handler(void *sock_id) 232 { 233 int s = (int)(uintptr_t)sock_id; 234 int ret; 235 char buffer_recv[BUF_SIZE]; 236 /* receive data is not null terminated */ 237 int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); 238 239 while (bytes > 0) { 240 buffer_recv[bytes] = 0; 241 int i, j; 242 char buffer[BUF_SIZE]; 243 for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) { 244 buffer[j] = buffer_recv[i]; 245 j += !isspace(buffer_recv[i]); 246 } 247 buffer[j] = 0; 248 ret = parse_client_request(buffer, j, s); 249 if (ret < 0) { 250 ret = send_error_response(s, ret); 251 if (ret < 0) 252 printf("\nCould not send error response\n"); 253 } 254 bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); 255 } 256 close(s); 257 return NULL; 258 } 259 260 #endif /* !RTE_EXEC_ENV_WINDOWS */ 261