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 struct sockaddr_un addrs; 87 #endif /* !RTE_EXEC_ENV_WINDOWS */ 88 89 if (!strchr(params, ':')) { 90 fprintf(stderr, "Invalid data\n"); 91 return -1; 92 } 93 #ifndef RTE_EXEC_ENV_WINDOWS 94 strlcpy(data, strchr(params, ':'), sizeof(data)); 95 memcpy(data, &data[strlen(":\"")], strlen(data)); 96 if (!strchr(data, '\"')) { 97 fprintf(stderr, "Invalid client data\n"); 98 return -1; 99 } 100 *strchr(data, '\"') = 0; 101 102 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 103 if (fd < 0) { 104 perror("Failed to open socket"); 105 return -1; 106 } 107 addrs.sun_family = AF_UNIX; 108 strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path)); 109 110 if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) { 111 perror("\nClient connection error\n"); 112 close(fd); 113 return -1; 114 } 115 pthread_create(&th, NULL, &legacy_client_handler, 116 (void *)(uintptr_t)fd); 117 #endif /* !RTE_EXEC_ENV_WINDOWS */ 118 return 0; 119 } 120 121 #ifndef RTE_EXEC_ENV_WINDOWS 122 123 static int 124 send_error_response(int s, int err) 125 { 126 const char *desc; 127 char out_buf[100000]; 128 129 switch (err) { 130 case -ENOMEM: 131 desc = "Memory Allocation Error"; 132 break; 133 case -EINVAL: 134 desc = "Invalid Argument 404"; 135 break; 136 case -EPERM: 137 desc = "Unknown"; 138 break; 139 default: 140 /* Default case keeps behaviour of Telemetry library */ 141 printf("\nInvalid error type: %d\n", err); 142 return -EINVAL; 143 } 144 int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": " 145 "\"Status Error: %s\", \"data\": null}", desc); 146 if (write(s, out_buf, used) < 0) { 147 perror("Error writing to socket"); 148 return -1; 149 } 150 return 0; 151 } 152 153 static void 154 perform_command(telemetry_legacy_cb fn, const char *param, int s) 155 { 156 char out_buf[100000]; 157 int ret, used = 0; 158 159 ret = fn("", param, out_buf, sizeof(out_buf)); 160 if (ret < 0) { 161 ret = send_error_response(s, ret); 162 if (ret < 0) 163 printf("\nCould not send error response\n"); 164 return; 165 } 166 used += ret; 167 if (write(s, out_buf, used) < 0) 168 perror("Error writing to socket"); 169 } 170 171 static int 172 parse_client_request(char *buffer, int buf_len, int s) 173 { 174 int i; 175 char *data = buffer + buf_len; 176 telemetry_legacy_cb fn = NULL; 177 const char *valid_sep = ",}"; 178 if (buffer[0] != '{' || buffer[buf_len - 1] != '}') 179 return -EPERM; 180 181 if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD) 182 && strstr(buffer, CLIENTS_DATA)) 183 return 0; 184 185 for (i = 0; i < num_legacy_callbacks; i++) { 186 char *action_ptr = strstr(buffer, callbacks[i].action); 187 char *cmd_ptr = strstr(buffer, callbacks[i].cmd); 188 char *data_ptr = strstr(buffer, callbacks[i].data); 189 if (!action_ptr || !cmd_ptr || !data_ptr) 190 continue; 191 192 char action_sep = action_ptr[strlen(callbacks[i].action)]; 193 char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)]; 194 if (!(strchr(valid_sep, action_sep) && strchr(valid_sep, 195 cmd_sep))) 196 return -EPERM; 197 char data_sep; 198 199 if (!strchr(data_ptr, '{')) 200 data_sep = data_ptr[strlen(callbacks[i].data)]; 201 else { 202 if (!strchr(data_ptr, '}')) 203 return -EINVAL; 204 char *data_end = strchr(data_ptr, '}'); 205 data = data_ptr + strlen(DATA_REQ_LABEL); 206 data_sep = data_end[1]; 207 data_end[1] = 0; 208 } 209 if (!strchr(valid_sep, data_sep)) 210 return -EPERM; 211 fn = callbacks[i].fn; 212 break; 213 } 214 215 if (!fn) 216 return -EINVAL; 217 perform_command(fn, data, s); 218 return 0; 219 } 220 221 void * 222 legacy_client_handler(void *sock_id) 223 { 224 int s = (int)(uintptr_t)sock_id; 225 int ret; 226 char buffer_recv[BUF_SIZE]; 227 /* receive data is not null terminated */ 228 int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); 229 230 while (bytes > 0) { 231 buffer_recv[bytes] = 0; 232 int i, j; 233 char buffer[BUF_SIZE]; 234 for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) { 235 buffer[j] = buffer_recv[i]; 236 j += !isspace(buffer_recv[i]); 237 } 238 buffer[j] = 0; 239 ret = parse_client_request(buffer, j, s); 240 if (ret < 0) { 241 ret = send_error_response(s, ret); 242 if (ret < 0) 243 printf("\nCould not send error response\n"); 244 } 245 bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); 246 } 247 close(s); 248 return NULL; 249 } 250 251 #endif /* !RTE_EXEC_ENV_WINDOWS */ 252