199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2020 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson 599a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 699a2dd95SBruce Richardson #include <unistd.h> 799a2dd95SBruce Richardson #include <pthread.h> 899a2dd95SBruce Richardson #include <sys/socket.h> 999a2dd95SBruce Richardson #include <sys/un.h> 10d5252f7dSBruce Richardson #include <sys/stat.h> 1199a2dd95SBruce Richardson #include <dlfcn.h> 1299a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 1399a2dd95SBruce Richardson 1499a2dd95SBruce Richardson /* we won't link against libbsd, so just always use DPDKs-specific strlcpy */ 1599a2dd95SBruce Richardson #undef RTE_USE_LIBBSD 1699a2dd95SBruce Richardson #include <rte_string_fns.h> 1799a2dd95SBruce Richardson #include <rte_common.h> 1899a2dd95SBruce Richardson #include <rte_spinlock.h> 1999a2dd95SBruce Richardson #include <rte_log.h> 2099a2dd95SBruce Richardson 2199a2dd95SBruce Richardson #include "rte_telemetry.h" 2299a2dd95SBruce Richardson #include "telemetry_json.h" 2399a2dd95SBruce Richardson #include "telemetry_data.h" 2499a2dd95SBruce Richardson #include "telemetry_internal.h" 2599a2dd95SBruce Richardson 2699a2dd95SBruce Richardson #define MAX_CMD_LEN 56 2799a2dd95SBruce Richardson #define MAX_OUTPUT_LEN (1024 * 16) 2899a2dd95SBruce Richardson #define MAX_CONNECTIONS 10 2999a2dd95SBruce Richardson 3099a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 3199a2dd95SBruce Richardson static void * 3299a2dd95SBruce Richardson client_handler(void *socket); 3399a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 3499a2dd95SBruce Richardson 3599a2dd95SBruce Richardson struct cmd_callback { 3699a2dd95SBruce Richardson char cmd[MAX_CMD_LEN]; 3799a2dd95SBruce Richardson telemetry_cb fn; 38d2671e64SRadu Nicolau char help[RTE_TEL_MAX_STRING_LEN]; 3999a2dd95SBruce Richardson }; 4099a2dd95SBruce Richardson 4199a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 4299a2dd95SBruce Richardson struct socket { 4399a2dd95SBruce Richardson int sock; 4499a2dd95SBruce Richardson char path[sizeof(((struct sockaddr_un *)0)->sun_path)]; 4599a2dd95SBruce Richardson handler fn; 4699a2dd95SBruce Richardson uint16_t *num_clients; 4799a2dd95SBruce Richardson }; 4899a2dd95SBruce Richardson static struct socket v2_socket; /* socket for v2 telemetry */ 4999a2dd95SBruce Richardson static struct socket v1_socket; /* socket for v1 telemetry */ 5099a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 5199a2dd95SBruce Richardson 5299a2dd95SBruce Richardson static const char *telemetry_version; /* save rte_version */ 5399a2dd95SBruce Richardson static const char *socket_dir; /* runtime directory */ 5499a2dd95SBruce Richardson static rte_cpuset_t *thread_cpuset; 5599a2dd95SBruce Richardson static rte_log_fn rte_log_ptr; 5699a2dd95SBruce Richardson static uint32_t logtype; 5799a2dd95SBruce Richardson 5899a2dd95SBruce Richardson #define TMTY_LOG(l, ...) \ 5999a2dd95SBruce Richardson rte_log_ptr(RTE_LOG_ ## l, logtype, "TELEMETRY: " __VA_ARGS__) 6099a2dd95SBruce Richardson 6199a2dd95SBruce Richardson /* list of command callbacks, with one command registered by default */ 6264307fadSDavid Marchand static struct cmd_callback *callbacks; 6399a2dd95SBruce Richardson static int num_callbacks; /* How many commands are registered */ 6499a2dd95SBruce Richardson /* Used when accessing or modifying list of command callbacks */ 6599a2dd95SBruce Richardson static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; 6699a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 6799a2dd95SBruce Richardson static uint16_t v2_clients; 6899a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 6999a2dd95SBruce Richardson 7099a2dd95SBruce Richardson int 7199a2dd95SBruce Richardson rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help) 7299a2dd95SBruce Richardson { 7364307fadSDavid Marchand struct cmd_callback *new_callbacks; 7499a2dd95SBruce Richardson int i = 0; 7599a2dd95SBruce Richardson 7699a2dd95SBruce Richardson if (strlen(cmd) >= MAX_CMD_LEN || fn == NULL || cmd[0] != '/' 77d2671e64SRadu Nicolau || strlen(help) >= RTE_TEL_MAX_STRING_LEN) 7899a2dd95SBruce Richardson return -EINVAL; 7999a2dd95SBruce Richardson 8099a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 8164307fadSDavid Marchand new_callbacks = realloc(callbacks, sizeof(callbacks[0]) * (num_callbacks + 1)); 8264307fadSDavid Marchand if (new_callbacks == NULL) { 8364307fadSDavid Marchand rte_spinlock_unlock(&callback_sl); 8464307fadSDavid Marchand return -ENOMEM; 8564307fadSDavid Marchand } 8664307fadSDavid Marchand callbacks = new_callbacks; 8764307fadSDavid Marchand 8899a2dd95SBruce Richardson while (i < num_callbacks && strcmp(cmd, callbacks[i].cmd) > 0) 8999a2dd95SBruce Richardson i++; 9099a2dd95SBruce Richardson if (i != num_callbacks) 9199a2dd95SBruce Richardson /* Move elements to keep the list alphabetical */ 9299a2dd95SBruce Richardson memmove(callbacks + i + 1, callbacks + i, 9399a2dd95SBruce Richardson sizeof(struct cmd_callback) * (num_callbacks - i)); 9499a2dd95SBruce Richardson 9599a2dd95SBruce Richardson strlcpy(callbacks[i].cmd, cmd, MAX_CMD_LEN); 9699a2dd95SBruce Richardson callbacks[i].fn = fn; 97d2671e64SRadu Nicolau strlcpy(callbacks[i].help, help, RTE_TEL_MAX_STRING_LEN); 9899a2dd95SBruce Richardson num_callbacks++; 9999a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 10099a2dd95SBruce Richardson 10199a2dd95SBruce Richardson return 0; 10299a2dd95SBruce Richardson } 10399a2dd95SBruce Richardson 10499a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 10599a2dd95SBruce Richardson 10699a2dd95SBruce Richardson static int 10799a2dd95SBruce Richardson list_commands(const char *cmd __rte_unused, const char *params __rte_unused, 10899a2dd95SBruce Richardson struct rte_tel_data *d) 10999a2dd95SBruce Richardson { 11099a2dd95SBruce Richardson int i; 11199a2dd95SBruce Richardson 11299a2dd95SBruce Richardson rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); 11304896027SCiara Power rte_spinlock_lock(&callback_sl); 11499a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 11599a2dd95SBruce Richardson rte_tel_data_add_array_string(d, callbacks[i].cmd); 11604896027SCiara Power rte_spinlock_unlock(&callback_sl); 11799a2dd95SBruce Richardson return 0; 11899a2dd95SBruce Richardson } 11999a2dd95SBruce Richardson 12099a2dd95SBruce Richardson static int 12199a2dd95SBruce Richardson json_info(const char *cmd __rte_unused, const char *params __rte_unused, 12299a2dd95SBruce Richardson struct rte_tel_data *d) 12399a2dd95SBruce Richardson { 12499a2dd95SBruce Richardson rte_tel_data_start_dict(d); 12599a2dd95SBruce Richardson rte_tel_data_add_dict_string(d, "version", telemetry_version); 12699a2dd95SBruce Richardson rte_tel_data_add_dict_int(d, "pid", getpid()); 12799a2dd95SBruce Richardson rte_tel_data_add_dict_int(d, "max_output_len", MAX_OUTPUT_LEN); 12899a2dd95SBruce Richardson return 0; 12999a2dd95SBruce Richardson } 13099a2dd95SBruce Richardson 13199a2dd95SBruce Richardson static int 13299a2dd95SBruce Richardson command_help(const char *cmd __rte_unused, const char *params, 13399a2dd95SBruce Richardson struct rte_tel_data *d) 13499a2dd95SBruce Richardson { 13599a2dd95SBruce Richardson int i; 13699a2dd95SBruce Richardson 13799a2dd95SBruce Richardson if (!params) 13899a2dd95SBruce Richardson return -1; 13999a2dd95SBruce Richardson rte_tel_data_start_dict(d); 14099a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 14199a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 14299a2dd95SBruce Richardson if (strcmp(params, callbacks[i].cmd) == 0) { 14399a2dd95SBruce Richardson rte_tel_data_add_dict_string(d, params, 14499a2dd95SBruce Richardson callbacks[i].help); 14599a2dd95SBruce Richardson break; 14699a2dd95SBruce Richardson } 14799a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 14899a2dd95SBruce Richardson if (i == num_callbacks) 14999a2dd95SBruce Richardson return -1; 15099a2dd95SBruce Richardson return 0; 15199a2dd95SBruce Richardson } 15299a2dd95SBruce Richardson 15399a2dd95SBruce Richardson static int 15499a2dd95SBruce Richardson container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) 15599a2dd95SBruce Richardson { 15699a2dd95SBruce Richardson size_t used = 0; 15799a2dd95SBruce Richardson unsigned int i; 15899a2dd95SBruce Richardson 159d2671e64SRadu Nicolau if (d->type != RTE_TEL_DICT && d->type != RTE_TEL_ARRAY_U64 && 160d2671e64SRadu Nicolau d->type != RTE_TEL_ARRAY_INT && d->type != RTE_TEL_ARRAY_STRING) 16199a2dd95SBruce Richardson return snprintf(out_buf, buf_len, "null"); 16299a2dd95SBruce Richardson 16399a2dd95SBruce Richardson used = rte_tel_json_empty_array(out_buf, buf_len, 0); 16499a2dd95SBruce Richardson if (d->type == RTE_TEL_ARRAY_U64) 16599a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 16699a2dd95SBruce Richardson used = rte_tel_json_add_array_u64(out_buf, 16799a2dd95SBruce Richardson buf_len, used, 16899a2dd95SBruce Richardson d->data.array[i].u64val); 16999a2dd95SBruce Richardson if (d->type == RTE_TEL_ARRAY_INT) 17099a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 17199a2dd95SBruce Richardson used = rte_tel_json_add_array_int(out_buf, 17299a2dd95SBruce Richardson buf_len, used, 17399a2dd95SBruce Richardson d->data.array[i].ival); 17499a2dd95SBruce Richardson if (d->type == RTE_TEL_ARRAY_STRING) 17599a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 17699a2dd95SBruce Richardson used = rte_tel_json_add_array_string(out_buf, 17799a2dd95SBruce Richardson buf_len, used, 17899a2dd95SBruce Richardson d->data.array[i].sval); 179d2671e64SRadu Nicolau if (d->type == RTE_TEL_DICT) 180d2671e64SRadu Nicolau for (i = 0; i < d->data_len; i++) { 181d2671e64SRadu Nicolau const struct tel_dict_entry *v = &d->data.dict[i]; 182d2671e64SRadu Nicolau switch (v->type) { 183d2671e64SRadu Nicolau case RTE_TEL_STRING_VAL: 184d2671e64SRadu Nicolau used = rte_tel_json_add_obj_str(out_buf, 185d2671e64SRadu Nicolau buf_len, used, 186d2671e64SRadu Nicolau v->name, v->value.sval); 187d2671e64SRadu Nicolau break; 188d2671e64SRadu Nicolau case RTE_TEL_INT_VAL: 189d2671e64SRadu Nicolau used = rte_tel_json_add_obj_int(out_buf, 190d2671e64SRadu Nicolau buf_len, used, 191d2671e64SRadu Nicolau v->name, v->value.ival); 192d2671e64SRadu Nicolau break; 193d2671e64SRadu Nicolau case RTE_TEL_U64_VAL: 194d2671e64SRadu Nicolau used = rte_tel_json_add_obj_u64(out_buf, 195d2671e64SRadu Nicolau buf_len, used, 196d2671e64SRadu Nicolau v->name, v->value.u64val); 197d2671e64SRadu Nicolau break; 198d2671e64SRadu Nicolau case RTE_TEL_CONTAINER: 199d2671e64SRadu Nicolau { 200d2671e64SRadu Nicolau char temp[buf_len]; 201d2671e64SRadu Nicolau const struct container *cont = 202d2671e64SRadu Nicolau &v->value.container; 203d2671e64SRadu Nicolau if (container_to_json(cont->data, 204d2671e64SRadu Nicolau temp, buf_len) != 0) 205d2671e64SRadu Nicolau used = rte_tel_json_add_obj_json( 206d2671e64SRadu Nicolau out_buf, 207d2671e64SRadu Nicolau buf_len, used, 208d2671e64SRadu Nicolau v->name, temp); 209d2671e64SRadu Nicolau if (!cont->keep) 210d2671e64SRadu Nicolau rte_tel_data_free(cont->data); 211d2671e64SRadu Nicolau break; 212d2671e64SRadu Nicolau } 213d2671e64SRadu Nicolau } 214d2671e64SRadu Nicolau } 215d2671e64SRadu Nicolau 21699a2dd95SBruce Richardson return used; 21799a2dd95SBruce Richardson } 21899a2dd95SBruce Richardson 21999a2dd95SBruce Richardson static void 22099a2dd95SBruce Richardson output_json(const char *cmd, const struct rte_tel_data *d, int s) 22199a2dd95SBruce Richardson { 22299a2dd95SBruce Richardson char out_buf[MAX_OUTPUT_LEN]; 22399a2dd95SBruce Richardson 22499a2dd95SBruce Richardson char *cb_data_buf; 22599a2dd95SBruce Richardson size_t buf_len, prefix_used, used = 0; 22699a2dd95SBruce Richardson unsigned int i; 22799a2dd95SBruce Richardson 22899a2dd95SBruce Richardson RTE_BUILD_BUG_ON(sizeof(out_buf) < MAX_CMD_LEN + 22999a2dd95SBruce Richardson RTE_TEL_MAX_SINGLE_STRING_LEN + 10); 23099a2dd95SBruce Richardson switch (d->type) { 23199a2dd95SBruce Richardson case RTE_TEL_NULL: 23299a2dd95SBruce Richardson used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}", 23399a2dd95SBruce Richardson MAX_CMD_LEN, cmd ? cmd : "none"); 23499a2dd95SBruce Richardson break; 23599a2dd95SBruce Richardson case RTE_TEL_STRING: 23699a2dd95SBruce Richardson used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":\"%.*s\"}", 23799a2dd95SBruce Richardson MAX_CMD_LEN, cmd, 23899a2dd95SBruce Richardson RTE_TEL_MAX_SINGLE_STRING_LEN, d->data.str); 23999a2dd95SBruce Richardson break; 24099a2dd95SBruce Richardson case RTE_TEL_DICT: 24199a2dd95SBruce Richardson prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", 24299a2dd95SBruce Richardson MAX_CMD_LEN, cmd); 24399a2dd95SBruce Richardson cb_data_buf = &out_buf[prefix_used]; 24499a2dd95SBruce Richardson buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */ 24599a2dd95SBruce Richardson 24699a2dd95SBruce Richardson used = rte_tel_json_empty_obj(cb_data_buf, buf_len, 0); 24799a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) { 24899a2dd95SBruce Richardson const struct tel_dict_entry *v = &d->data.dict[i]; 24999a2dd95SBruce Richardson switch (v->type) { 25099a2dd95SBruce Richardson case RTE_TEL_STRING_VAL: 25199a2dd95SBruce Richardson used = rte_tel_json_add_obj_str(cb_data_buf, 25299a2dd95SBruce Richardson buf_len, used, 25399a2dd95SBruce Richardson v->name, v->value.sval); 25499a2dd95SBruce Richardson break; 25599a2dd95SBruce Richardson case RTE_TEL_INT_VAL: 25699a2dd95SBruce Richardson used = rte_tel_json_add_obj_int(cb_data_buf, 25799a2dd95SBruce Richardson buf_len, used, 25899a2dd95SBruce Richardson v->name, v->value.ival); 25999a2dd95SBruce Richardson break; 26099a2dd95SBruce Richardson case RTE_TEL_U64_VAL: 26199a2dd95SBruce Richardson used = rte_tel_json_add_obj_u64(cb_data_buf, 26299a2dd95SBruce Richardson buf_len, used, 26399a2dd95SBruce Richardson v->name, v->value.u64val); 26499a2dd95SBruce Richardson break; 26599a2dd95SBruce Richardson case RTE_TEL_CONTAINER: 26699a2dd95SBruce Richardson { 26799a2dd95SBruce Richardson char temp[buf_len]; 26899a2dd95SBruce Richardson const struct container *cont = 26999a2dd95SBruce Richardson &v->value.container; 27099a2dd95SBruce Richardson if (container_to_json(cont->data, 27199a2dd95SBruce Richardson temp, buf_len) != 0) 27299a2dd95SBruce Richardson used = rte_tel_json_add_obj_json( 27399a2dd95SBruce Richardson cb_data_buf, 27499a2dd95SBruce Richardson buf_len, used, 27599a2dd95SBruce Richardson v->name, temp); 27699a2dd95SBruce Richardson if (!cont->keep) 27799a2dd95SBruce Richardson rte_tel_data_free(cont->data); 27899a2dd95SBruce Richardson } 27999a2dd95SBruce Richardson } 28099a2dd95SBruce Richardson } 28199a2dd95SBruce Richardson used += prefix_used; 28299a2dd95SBruce Richardson used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); 28399a2dd95SBruce Richardson break; 28499a2dd95SBruce Richardson case RTE_TEL_ARRAY_STRING: 28599a2dd95SBruce Richardson case RTE_TEL_ARRAY_INT: 28699a2dd95SBruce Richardson case RTE_TEL_ARRAY_U64: 28799a2dd95SBruce Richardson case RTE_TEL_ARRAY_CONTAINER: 28899a2dd95SBruce Richardson prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", 28999a2dd95SBruce Richardson MAX_CMD_LEN, cmd); 29099a2dd95SBruce Richardson cb_data_buf = &out_buf[prefix_used]; 29199a2dd95SBruce Richardson buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */ 29299a2dd95SBruce Richardson 29399a2dd95SBruce Richardson used = rte_tel_json_empty_array(cb_data_buf, buf_len, 0); 29499a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 29599a2dd95SBruce Richardson if (d->type == RTE_TEL_ARRAY_STRING) 29699a2dd95SBruce Richardson used = rte_tel_json_add_array_string( 29799a2dd95SBruce Richardson cb_data_buf, 29899a2dd95SBruce Richardson buf_len, used, 29999a2dd95SBruce Richardson d->data.array[i].sval); 30099a2dd95SBruce Richardson else if (d->type == RTE_TEL_ARRAY_INT) 30199a2dd95SBruce Richardson used = rte_tel_json_add_array_int(cb_data_buf, 30299a2dd95SBruce Richardson buf_len, used, 30399a2dd95SBruce Richardson d->data.array[i].ival); 30499a2dd95SBruce Richardson else if (d->type == RTE_TEL_ARRAY_U64) 30599a2dd95SBruce Richardson used = rte_tel_json_add_array_u64(cb_data_buf, 30699a2dd95SBruce Richardson buf_len, used, 30799a2dd95SBruce Richardson d->data.array[i].u64val); 30899a2dd95SBruce Richardson else if (d->type == RTE_TEL_ARRAY_CONTAINER) { 30999a2dd95SBruce Richardson char temp[buf_len]; 31099a2dd95SBruce Richardson const struct container *rec_data = 31199a2dd95SBruce Richardson &d->data.array[i].container; 31299a2dd95SBruce Richardson if (container_to_json(rec_data->data, 31399a2dd95SBruce Richardson temp, buf_len) != 0) 31499a2dd95SBruce Richardson used = rte_tel_json_add_array_json( 31599a2dd95SBruce Richardson cb_data_buf, 31699a2dd95SBruce Richardson buf_len, used, temp); 31799a2dd95SBruce Richardson if (!rec_data->keep) 31899a2dd95SBruce Richardson rte_tel_data_free(rec_data->data); 31999a2dd95SBruce Richardson } 32099a2dd95SBruce Richardson used += prefix_used; 32199a2dd95SBruce Richardson used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); 32299a2dd95SBruce Richardson break; 32399a2dd95SBruce Richardson } 32499a2dd95SBruce Richardson if (write(s, out_buf, used) < 0) 32599a2dd95SBruce Richardson perror("Error writing to socket"); 32699a2dd95SBruce Richardson } 32799a2dd95SBruce Richardson 32899a2dd95SBruce Richardson static void 32999a2dd95SBruce Richardson perform_command(telemetry_cb fn, const char *cmd, const char *param, int s) 33099a2dd95SBruce Richardson { 33199a2dd95SBruce Richardson struct rte_tel_data data; 33299a2dd95SBruce Richardson 33399a2dd95SBruce Richardson int ret = fn(cmd, param, &data); 33499a2dd95SBruce Richardson if (ret < 0) { 33599a2dd95SBruce Richardson char out_buf[MAX_CMD_LEN + 10]; 33699a2dd95SBruce Richardson int used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}", 33799a2dd95SBruce Richardson MAX_CMD_LEN, cmd ? cmd : "none"); 33899a2dd95SBruce Richardson if (write(s, out_buf, used) < 0) 33999a2dd95SBruce Richardson perror("Error writing to socket"); 34099a2dd95SBruce Richardson return; 34199a2dd95SBruce Richardson } 34299a2dd95SBruce Richardson output_json(cmd, &data, s); 34399a2dd95SBruce Richardson } 34499a2dd95SBruce Richardson 34599a2dd95SBruce Richardson static int 34699a2dd95SBruce Richardson unknown_command(const char *cmd __rte_unused, const char *params __rte_unused, 34799a2dd95SBruce Richardson struct rte_tel_data *d) 34899a2dd95SBruce Richardson { 34999a2dd95SBruce Richardson return d->type = RTE_TEL_NULL; 35099a2dd95SBruce Richardson } 35199a2dd95SBruce Richardson 35299a2dd95SBruce Richardson static void * 35399a2dd95SBruce Richardson client_handler(void *sock_id) 35499a2dd95SBruce Richardson { 35599a2dd95SBruce Richardson int s = (int)(uintptr_t)sock_id; 35699a2dd95SBruce Richardson char buffer[1024]; 35799a2dd95SBruce Richardson char info_str[1024]; 35899a2dd95SBruce Richardson snprintf(info_str, sizeof(info_str), 35999a2dd95SBruce Richardson "{\"version\":\"%s\",\"pid\":%d,\"max_output_len\":%d}", 36099a2dd95SBruce Richardson telemetry_version, getpid(), MAX_OUTPUT_LEN); 36199a2dd95SBruce Richardson if (write(s, info_str, strlen(info_str)) < 0) { 36299a2dd95SBruce Richardson close(s); 36399a2dd95SBruce Richardson return NULL; 36499a2dd95SBruce Richardson } 36599a2dd95SBruce Richardson 36699a2dd95SBruce Richardson /* receive data is not null terminated */ 36799a2dd95SBruce Richardson int bytes = read(s, buffer, sizeof(buffer) - 1); 36899a2dd95SBruce Richardson while (bytes > 0) { 36999a2dd95SBruce Richardson buffer[bytes] = 0; 37099a2dd95SBruce Richardson const char *cmd = strtok(buffer, ","); 37199a2dd95SBruce Richardson const char *param = strtok(NULL, "\0"); 37299a2dd95SBruce Richardson telemetry_cb fn = unknown_command; 37399a2dd95SBruce Richardson int i; 37499a2dd95SBruce Richardson 37599a2dd95SBruce Richardson if (cmd && strlen(cmd) < MAX_CMD_LEN) { 37699a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 37799a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 37899a2dd95SBruce Richardson if (strcmp(cmd, callbacks[i].cmd) == 0) { 37999a2dd95SBruce Richardson fn = callbacks[i].fn; 38099a2dd95SBruce Richardson break; 38199a2dd95SBruce Richardson } 38299a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 38399a2dd95SBruce Richardson } 38499a2dd95SBruce Richardson perform_command(fn, cmd, param, s); 38599a2dd95SBruce Richardson 38699a2dd95SBruce Richardson bytes = read(s, buffer, sizeof(buffer) - 1); 38799a2dd95SBruce Richardson } 38899a2dd95SBruce Richardson close(s); 38999a2dd95SBruce Richardson __atomic_sub_fetch(&v2_clients, 1, __ATOMIC_RELAXED); 39099a2dd95SBruce Richardson return NULL; 39199a2dd95SBruce Richardson } 39299a2dd95SBruce Richardson 39399a2dd95SBruce Richardson static void * 39499a2dd95SBruce Richardson socket_listener(void *socket) 39599a2dd95SBruce Richardson { 39699a2dd95SBruce Richardson while (1) { 39799a2dd95SBruce Richardson pthread_t th; 398c53a5f3eSChengwen Feng int rc; 39999a2dd95SBruce Richardson struct socket *s = (struct socket *)socket; 40099a2dd95SBruce Richardson int s_accepted = accept(s->sock, NULL, NULL); 40199a2dd95SBruce Richardson if (s_accepted < 0) { 40299a2dd95SBruce Richardson TMTY_LOG(ERR, "Error with accept, telemetry thread quitting\n"); 40399a2dd95SBruce Richardson return NULL; 40499a2dd95SBruce Richardson } 40599a2dd95SBruce Richardson if (s->num_clients != NULL) { 40699a2dd95SBruce Richardson uint16_t conns = __atomic_load_n(s->num_clients, 40799a2dd95SBruce Richardson __ATOMIC_RELAXED); 40899a2dd95SBruce Richardson if (conns >= MAX_CONNECTIONS) { 40999a2dd95SBruce Richardson close(s_accepted); 41099a2dd95SBruce Richardson continue; 41199a2dd95SBruce Richardson } 41299a2dd95SBruce Richardson __atomic_add_fetch(s->num_clients, 1, 41399a2dd95SBruce Richardson __ATOMIC_RELAXED); 41499a2dd95SBruce Richardson } 415c53a5f3eSChengwen Feng rc = pthread_create(&th, NULL, s->fn, 416c53a5f3eSChengwen Feng (void *)(uintptr_t)s_accepted); 417c53a5f3eSChengwen Feng if (rc != 0) { 418c53a5f3eSChengwen Feng TMTY_LOG(ERR, "Error with create client thread: %s\n", 419c53a5f3eSChengwen Feng strerror(rc)); 420c53a5f3eSChengwen Feng close(s_accepted); 421c53a5f3eSChengwen Feng if (s->num_clients != NULL) 422c53a5f3eSChengwen Feng __atomic_sub_fetch(s->num_clients, 1, 423c53a5f3eSChengwen Feng __ATOMIC_RELAXED); 424c53a5f3eSChengwen Feng continue; 425c53a5f3eSChengwen Feng } 42699a2dd95SBruce Richardson pthread_detach(th); 42799a2dd95SBruce Richardson } 42899a2dd95SBruce Richardson return NULL; 42999a2dd95SBruce Richardson } 43099a2dd95SBruce Richardson 43199a2dd95SBruce Richardson static inline char * 43299a2dd95SBruce Richardson get_socket_path(const char *runtime_dir, const int version) 43399a2dd95SBruce Richardson { 43499a2dd95SBruce Richardson static char path[PATH_MAX]; 43599a2dd95SBruce Richardson snprintf(path, sizeof(path), "%s/dpdk_telemetry.v%d", 43699a2dd95SBruce Richardson strlen(runtime_dir) ? runtime_dir : "/tmp", version); 43799a2dd95SBruce Richardson return path; 43899a2dd95SBruce Richardson } 43999a2dd95SBruce Richardson 44099a2dd95SBruce Richardson static void 44199a2dd95SBruce Richardson unlink_sockets(void) 44299a2dd95SBruce Richardson { 44399a2dd95SBruce Richardson if (v2_socket.path[0]) 44499a2dd95SBruce Richardson unlink(v2_socket.path); 44599a2dd95SBruce Richardson if (v1_socket.path[0]) 44699a2dd95SBruce Richardson unlink(v1_socket.path); 44799a2dd95SBruce Richardson } 44899a2dd95SBruce Richardson 44999a2dd95SBruce Richardson static int 45099a2dd95SBruce Richardson create_socket(char *path) 45199a2dd95SBruce Richardson { 45299a2dd95SBruce Richardson int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); 45399a2dd95SBruce Richardson if (sock < 0) { 45499a2dd95SBruce Richardson TMTY_LOG(ERR, "Error with socket creation, %s\n", strerror(errno)); 45599a2dd95SBruce Richardson return -1; 45699a2dd95SBruce Richardson } 45799a2dd95SBruce Richardson 45899a2dd95SBruce Richardson struct sockaddr_un sun = {.sun_family = AF_UNIX}; 45999a2dd95SBruce Richardson strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 46085e21b77SBruce Richardson TMTY_LOG(DEBUG, "Attempting socket bind to path '%s'\n", path); 46185e21b77SBruce Richardson 46299a2dd95SBruce Richardson if (bind(sock, (void *) &sun, sizeof(sun)) < 0) { 463d5252f7dSBruce Richardson struct stat st; 464d5252f7dSBruce Richardson 46585e21b77SBruce Richardson TMTY_LOG(DEBUG, "Initial bind to socket '%s' failed.\n", path); 46685e21b77SBruce Richardson 46785e21b77SBruce Richardson /* first check if we have a runtime dir */ 46885e21b77SBruce Richardson if (stat(socket_dir, &st) < 0 || !S_ISDIR(st.st_mode)) { 469d5252f7dSBruce Richardson TMTY_LOG(ERR, "Cannot access DPDK runtime directory: %s\n", socket_dir); 47085e21b77SBruce Richardson close(sock); 47185e21b77SBruce Richardson return -ENOENT; 47285e21b77SBruce Richardson } 47385e21b77SBruce Richardson 47485e21b77SBruce Richardson /* check if current socket is active */ 47585e21b77SBruce Richardson if (connect(sock, (void *)&sun, sizeof(sun)) == 0) { 47685e21b77SBruce Richardson close(sock); 47785e21b77SBruce Richardson return -EADDRINUSE; 47885e21b77SBruce Richardson } 47985e21b77SBruce Richardson 48085e21b77SBruce Richardson /* socket is not active, delete and attempt rebind */ 48185e21b77SBruce Richardson TMTY_LOG(DEBUG, "Attempting unlink and retrying bind\n"); 48285e21b77SBruce Richardson unlink(sun.sun_path); 48385e21b77SBruce Richardson if (bind(sock, (void *) &sun, sizeof(sun)) < 0) { 48485e21b77SBruce Richardson TMTY_LOG(ERR, "Error binding socket: %s\n", strerror(errno)); 48585e21b77SBruce Richardson close(sock); 48685e21b77SBruce Richardson return -errno; /* if unlink failed, this will be -EADDRINUSE as above */ 48785e21b77SBruce Richardson } 48899a2dd95SBruce Richardson } 48999a2dd95SBruce Richardson 49099a2dd95SBruce Richardson if (listen(sock, 1) < 0) { 49199a2dd95SBruce Richardson TMTY_LOG(ERR, "Error calling listen for socket: %s\n", strerror(errno)); 49285e21b77SBruce Richardson unlink(sun.sun_path); 49385e21b77SBruce Richardson close(sock); 49485e21b77SBruce Richardson return -errno; 49599a2dd95SBruce Richardson } 49685e21b77SBruce Richardson TMTY_LOG(DEBUG, "Socket creation and binding ok\n"); 49799a2dd95SBruce Richardson 49899a2dd95SBruce Richardson return sock; 49999a2dd95SBruce Richardson } 50099a2dd95SBruce Richardson 501027c931bSThomas Monjalon static void 502027c931bSThomas Monjalon set_thread_name(pthread_t id __rte_unused, const char *name __rte_unused) 503027c931bSThomas Monjalon { 504027c931bSThomas Monjalon #if defined RTE_EXEC_ENV_LINUX && defined __GLIBC__ && defined __GLIBC_PREREQ 505027c931bSThomas Monjalon #if __GLIBC_PREREQ(2, 12) 506027c931bSThomas Monjalon pthread_setname_np(id, name); 507027c931bSThomas Monjalon #endif 508027c931bSThomas Monjalon #elif defined RTE_EXEC_ENV_FREEBSD 509027c931bSThomas Monjalon pthread_set_name_np(id, name); 510027c931bSThomas Monjalon #endif 511027c931bSThomas Monjalon } 512027c931bSThomas Monjalon 51399a2dd95SBruce Richardson static int 51499a2dd95SBruce Richardson telemetry_legacy_init(void) 51599a2dd95SBruce Richardson { 51699a2dd95SBruce Richardson pthread_t t_old; 517c53a5f3eSChengwen Feng int rc; 51899a2dd95SBruce Richardson 51999a2dd95SBruce Richardson if (num_legacy_callbacks == 1) { 52099a2dd95SBruce Richardson TMTY_LOG(WARNING, "No legacy callbacks, legacy socket not created\n"); 52199a2dd95SBruce Richardson return -1; 52299a2dd95SBruce Richardson } 52399a2dd95SBruce Richardson 52499a2dd95SBruce Richardson v1_socket.fn = legacy_client_handler; 52599a2dd95SBruce Richardson if ((size_t) snprintf(v1_socket.path, sizeof(v1_socket.path), 52699a2dd95SBruce Richardson "%s/telemetry", socket_dir) >= sizeof(v1_socket.path)) { 52799a2dd95SBruce Richardson TMTY_LOG(ERR, "Error with socket binding, path too long\n"); 52899a2dd95SBruce Richardson return -1; 52999a2dd95SBruce Richardson } 53099a2dd95SBruce Richardson v1_socket.sock = create_socket(v1_socket.path); 53185e21b77SBruce Richardson if (v1_socket.sock < 0) { 53285e21b77SBruce Richardson v1_socket.path[0] = '\0'; 53399a2dd95SBruce Richardson return -1; 53485e21b77SBruce Richardson } 535c53a5f3eSChengwen Feng rc = pthread_create(&t_old, NULL, socket_listener, &v1_socket); 536c53a5f3eSChengwen Feng if (rc != 0) { 537*7be78d02SJosh Soref TMTY_LOG(ERR, "Error with create legacy socket thread: %s\n", 538c53a5f3eSChengwen Feng strerror(rc)); 539c53a5f3eSChengwen Feng close(v1_socket.sock); 540c53a5f3eSChengwen Feng v1_socket.sock = -1; 541c53a5f3eSChengwen Feng unlink(v1_socket.path); 542c53a5f3eSChengwen Feng v1_socket.path[0] = '\0'; 543c53a5f3eSChengwen Feng return -1; 544c53a5f3eSChengwen Feng } 54599a2dd95SBruce Richardson pthread_setaffinity_np(t_old, sizeof(*thread_cpuset), thread_cpuset); 546027c931bSThomas Monjalon set_thread_name(t_old, "telemetry-v1"); 54799a2dd95SBruce Richardson TMTY_LOG(DEBUG, "Legacy telemetry socket initialized ok\n"); 54871ecc415SStephen Hemminger pthread_detach(t_old); 54999a2dd95SBruce Richardson return 0; 55099a2dd95SBruce Richardson } 55199a2dd95SBruce Richardson 55299a2dd95SBruce Richardson static int 55399a2dd95SBruce Richardson telemetry_v2_init(void) 55499a2dd95SBruce Richardson { 55585e21b77SBruce Richardson char spath[sizeof(v2_socket.path)]; 55699a2dd95SBruce Richardson pthread_t t_new; 55785e21b77SBruce Richardson short suffix = 0; 558c53a5f3eSChengwen Feng int rc; 55999a2dd95SBruce Richardson 56099a2dd95SBruce Richardson v2_socket.num_clients = &v2_clients; 56199a2dd95SBruce Richardson rte_telemetry_register_cmd("/", list_commands, 56299a2dd95SBruce Richardson "Returns list of available commands, Takes no parameters"); 56399a2dd95SBruce Richardson rte_telemetry_register_cmd("/info", json_info, 56499a2dd95SBruce Richardson "Returns DPDK Telemetry information. Takes no parameters"); 56599a2dd95SBruce Richardson rte_telemetry_register_cmd("/help", command_help, 56699a2dd95SBruce Richardson "Returns help text for a command. Parameters: string command"); 56799a2dd95SBruce Richardson v2_socket.fn = client_handler; 56885e21b77SBruce Richardson if (strlcpy(spath, get_socket_path(socket_dir, 2), sizeof(spath)) >= sizeof(spath)) { 56999a2dd95SBruce Richardson TMTY_LOG(ERR, "Error with socket binding, path too long\n"); 57099a2dd95SBruce Richardson return -1; 57199a2dd95SBruce Richardson } 57285e21b77SBruce Richardson memcpy(v2_socket.path, spath, sizeof(v2_socket.path)); 57399a2dd95SBruce Richardson 57499a2dd95SBruce Richardson v2_socket.sock = create_socket(v2_socket.path); 57585e21b77SBruce Richardson while (v2_socket.sock < 0) { 57685e21b77SBruce Richardson /* bail out on unexpected error, or suffix wrap-around */ 57785e21b77SBruce Richardson if (v2_socket.sock != -EADDRINUSE || suffix < 0) { 57885e21b77SBruce Richardson v2_socket.path[0] = '\0'; /* clear socket path */ 57999a2dd95SBruce Richardson return -1; 58085e21b77SBruce Richardson } 58185e21b77SBruce Richardson /* add a suffix to the path if the basic version fails */ 58285e21b77SBruce Richardson if (snprintf(v2_socket.path, sizeof(v2_socket.path), "%s:%d", 58385e21b77SBruce Richardson spath, ++suffix) >= (int)sizeof(v2_socket.path)) { 58485e21b77SBruce Richardson TMTY_LOG(ERR, "Error with socket binding, path too long\n"); 58585e21b77SBruce Richardson return -1; 58685e21b77SBruce Richardson } 58785e21b77SBruce Richardson v2_socket.sock = create_socket(v2_socket.path); 58885e21b77SBruce Richardson } 589c53a5f3eSChengwen Feng rc = pthread_create(&t_new, NULL, socket_listener, &v2_socket); 590c53a5f3eSChengwen Feng if (rc != 0) { 591c53a5f3eSChengwen Feng TMTY_LOG(ERR, "Error with create socket thread: %s\n", 592c53a5f3eSChengwen Feng strerror(rc)); 593c53a5f3eSChengwen Feng close(v2_socket.sock); 594c53a5f3eSChengwen Feng v2_socket.sock = -1; 595c53a5f3eSChengwen Feng unlink(v2_socket.path); 596c53a5f3eSChengwen Feng v2_socket.path[0] = '\0'; 597c53a5f3eSChengwen Feng return -1; 598c53a5f3eSChengwen Feng } 59999a2dd95SBruce Richardson pthread_setaffinity_np(t_new, sizeof(*thread_cpuset), thread_cpuset); 600027c931bSThomas Monjalon set_thread_name(t_new, "telemetry-v2"); 60171ecc415SStephen Hemminger pthread_detach(t_new); 60299a2dd95SBruce Richardson atexit(unlink_sockets); 60399a2dd95SBruce Richardson 60499a2dd95SBruce Richardson return 0; 60599a2dd95SBruce Richardson } 60699a2dd95SBruce Richardson 60799a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 60899a2dd95SBruce Richardson 60999a2dd95SBruce Richardson int32_t 61099a2dd95SBruce Richardson rte_telemetry_init(const char *runtime_dir, const char *rte_version, rte_cpuset_t *cpuset, 61199a2dd95SBruce Richardson rte_log_fn log_fn, uint32_t registered_logtype) 61299a2dd95SBruce Richardson { 61399a2dd95SBruce Richardson telemetry_version = rte_version; 61499a2dd95SBruce Richardson socket_dir = runtime_dir; 61599a2dd95SBruce Richardson thread_cpuset = cpuset; 61699a2dd95SBruce Richardson rte_log_ptr = log_fn; 61799a2dd95SBruce Richardson logtype = registered_logtype; 61899a2dd95SBruce Richardson 61999a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 62099a2dd95SBruce Richardson if (telemetry_v2_init() != 0) 62199a2dd95SBruce Richardson return -1; 62299a2dd95SBruce Richardson TMTY_LOG(DEBUG, "Telemetry initialized ok\n"); 62399a2dd95SBruce Richardson telemetry_legacy_init(); 62499a2dd95SBruce Richardson #endif /* RTE_EXEC_ENV_WINDOWS */ 62599a2dd95SBruce Richardson 62699a2dd95SBruce Richardson return 0; 62799a2dd95SBruce Richardson } 628