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