199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2020 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson 5b64b2a6cSBruce Richardson #include <ctype.h> 672b452c5SDmitry Kozlyuk #include <errno.h> 772b452c5SDmitry Kozlyuk #include <stdlib.h> 899a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 999a2dd95SBruce Richardson #include <unistd.h> 1099a2dd95SBruce Richardson #include <pthread.h> 1199a2dd95SBruce Richardson #include <sys/socket.h> 1299a2dd95SBruce Richardson #include <sys/un.h> 13d5252f7dSBruce Richardson #include <sys/stat.h> 1499a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 1599a2dd95SBruce Richardson 1699a2dd95SBruce Richardson /* we won't link against libbsd, so just always use DPDKs-specific strlcpy */ 1799a2dd95SBruce Richardson #undef RTE_USE_LIBBSD 1899a2dd95SBruce Richardson #include <rte_string_fns.h> 1999a2dd95SBruce Richardson #include <rte_common.h> 2099a2dd95SBruce Richardson #include <rte_spinlock.h> 2199a2dd95SBruce Richardson #include <rte_log.h> 2299a2dd95SBruce Richardson 2399a2dd95SBruce Richardson #include "rte_telemetry.h" 2499a2dd95SBruce Richardson #include "telemetry_json.h" 2599a2dd95SBruce Richardson #include "telemetry_data.h" 2699a2dd95SBruce Richardson #include "telemetry_internal.h" 2799a2dd95SBruce Richardson 2899a2dd95SBruce Richardson #define MAX_CMD_LEN 56 2999a2dd95SBruce Richardson #define MAX_OUTPUT_LEN (1024 * 16) 3099a2dd95SBruce Richardson #define MAX_CONNECTIONS 10 3199a2dd95SBruce Richardson 3299a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 3399a2dd95SBruce Richardson static void * 3499a2dd95SBruce Richardson client_handler(void *socket); 3599a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 3699a2dd95SBruce Richardson 3799a2dd95SBruce Richardson struct cmd_callback { 3899a2dd95SBruce Richardson char cmd[MAX_CMD_LEN]; 3999a2dd95SBruce Richardson telemetry_cb fn; 40*ceb5914cSRobin Jarry telemetry_arg_cb fn_arg; 41*ceb5914cSRobin Jarry void *arg; 42d2671e64SRadu Nicolau char help[RTE_TEL_MAX_STRING_LEN]; 4399a2dd95SBruce Richardson }; 4499a2dd95SBruce Richardson 4599a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 4699a2dd95SBruce Richardson struct socket { 4799a2dd95SBruce Richardson int sock; 4899a2dd95SBruce Richardson char path[sizeof(((struct sockaddr_un *)0)->sun_path)]; 4999a2dd95SBruce Richardson handler fn; 503560f9a4STyler Retzlaff RTE_ATOMIC(uint16_t) *num_clients; 5199a2dd95SBruce Richardson }; 5299a2dd95SBruce Richardson static struct socket v2_socket; /* socket for v2 telemetry */ 5399a2dd95SBruce Richardson static struct socket v1_socket; /* socket for v1 telemetry */ 5499a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 5599a2dd95SBruce Richardson 5699a2dd95SBruce Richardson static const char *telemetry_version; /* save rte_version */ 5799a2dd95SBruce Richardson static const char *socket_dir; /* runtime directory */ 5899a2dd95SBruce Richardson static rte_cpuset_t *thread_cpuset; 5999a2dd95SBruce Richardson 6068150b90SBruce Richardson RTE_LOG_REGISTER_DEFAULT(logtype, WARNING); 6165b078dcSDavid Marchand #define RTE_LOGTYPE_TELEMETRY logtype 6265b078dcSDavid Marchand #define TMTY_LOG_LINE(l, ...) RTE_LOG_LINE(l, TELEMETRY, "" __VA_ARGS__) 6399a2dd95SBruce Richardson 6499a2dd95SBruce Richardson /* list of command callbacks, with one command registered by default */ 6564307fadSDavid Marchand static struct cmd_callback *callbacks; 6699a2dd95SBruce Richardson static int num_callbacks; /* How many commands are registered */ 6799a2dd95SBruce Richardson /* Used when accessing or modifying list of command callbacks */ 6899a2dd95SBruce Richardson static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; 6999a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 703560f9a4STyler Retzlaff static RTE_ATOMIC(uint16_t) v2_clients; 7199a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 7299a2dd95SBruce Richardson 73*ceb5914cSRobin Jarry static int 74*ceb5914cSRobin Jarry register_cmd(const char *cmd, const char *help, 75*ceb5914cSRobin Jarry telemetry_cb fn, telemetry_arg_cb fn_arg, void *arg) 7699a2dd95SBruce Richardson { 7764307fadSDavid Marchand struct cmd_callback *new_callbacks; 78b64b2a6cSBruce Richardson const char *cmdp = cmd; 7999a2dd95SBruce Richardson int i = 0; 8099a2dd95SBruce Richardson 81*ceb5914cSRobin Jarry if (strlen(cmd) >= MAX_CMD_LEN || (fn == NULL && fn_arg == NULL) || cmd[0] != '/' 82d2671e64SRadu Nicolau || strlen(help) >= RTE_TEL_MAX_STRING_LEN) 8399a2dd95SBruce Richardson return -EINVAL; 8499a2dd95SBruce Richardson 85b64b2a6cSBruce Richardson while (*cmdp != '\0') { 86b64b2a6cSBruce Richardson if (!isalnum(*cmdp) && *cmdp != '_' && *cmdp != '/') 87b64b2a6cSBruce Richardson return -EINVAL; 88b64b2a6cSBruce Richardson cmdp++; 89b64b2a6cSBruce Richardson } 90b64b2a6cSBruce Richardson 9199a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 9264307fadSDavid Marchand new_callbacks = realloc(callbacks, sizeof(callbacks[0]) * (num_callbacks + 1)); 9364307fadSDavid Marchand if (new_callbacks == NULL) { 9464307fadSDavid Marchand rte_spinlock_unlock(&callback_sl); 9564307fadSDavid Marchand return -ENOMEM; 9664307fadSDavid Marchand } 9764307fadSDavid Marchand callbacks = new_callbacks; 9864307fadSDavid Marchand 9999a2dd95SBruce Richardson while (i < num_callbacks && strcmp(cmd, callbacks[i].cmd) > 0) 10099a2dd95SBruce Richardson i++; 10199a2dd95SBruce Richardson if (i != num_callbacks) 10299a2dd95SBruce Richardson /* Move elements to keep the list alphabetical */ 10399a2dd95SBruce Richardson memmove(callbacks + i + 1, callbacks + i, 10499a2dd95SBruce Richardson sizeof(struct cmd_callback) * (num_callbacks - i)); 10599a2dd95SBruce Richardson 10699a2dd95SBruce Richardson strlcpy(callbacks[i].cmd, cmd, MAX_CMD_LEN); 10799a2dd95SBruce Richardson callbacks[i].fn = fn; 108*ceb5914cSRobin Jarry callbacks[i].fn_arg = fn_arg; 109*ceb5914cSRobin Jarry callbacks[i].arg = arg; 110d2671e64SRadu Nicolau strlcpy(callbacks[i].help, help, RTE_TEL_MAX_STRING_LEN); 11199a2dd95SBruce Richardson num_callbacks++; 11299a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson return 0; 11599a2dd95SBruce Richardson } 11699a2dd95SBruce Richardson 117*ceb5914cSRobin Jarry int 118*ceb5914cSRobin Jarry rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help) 119*ceb5914cSRobin Jarry { 120*ceb5914cSRobin Jarry return register_cmd(cmd, help, fn, NULL, NULL); 121*ceb5914cSRobin Jarry } 122*ceb5914cSRobin Jarry 123*ceb5914cSRobin Jarry int 124*ceb5914cSRobin Jarry rte_telemetry_register_cmd_arg(const char *cmd, telemetry_arg_cb fn, void *arg, const char *help) 125*ceb5914cSRobin Jarry { 126*ceb5914cSRobin Jarry return register_cmd(cmd, help, NULL, fn, arg); 127*ceb5914cSRobin Jarry } 128*ceb5914cSRobin Jarry 12999a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 13099a2dd95SBruce Richardson 13199a2dd95SBruce Richardson static int 13299a2dd95SBruce Richardson list_commands(const char *cmd __rte_unused, const char *params __rte_unused, 13399a2dd95SBruce Richardson struct rte_tel_data *d) 13499a2dd95SBruce Richardson { 13599a2dd95SBruce Richardson int i; 13699a2dd95SBruce Richardson 13799a2dd95SBruce Richardson rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); 13804896027SCiara Power rte_spinlock_lock(&callback_sl); 13999a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 14099a2dd95SBruce Richardson rte_tel_data_add_array_string(d, callbacks[i].cmd); 14104896027SCiara Power rte_spinlock_unlock(&callback_sl); 14299a2dd95SBruce Richardson return 0; 14399a2dd95SBruce Richardson } 14499a2dd95SBruce Richardson 14599a2dd95SBruce Richardson static int 14699a2dd95SBruce Richardson json_info(const char *cmd __rte_unused, const char *params __rte_unused, 14799a2dd95SBruce Richardson struct rte_tel_data *d) 14899a2dd95SBruce Richardson { 14999a2dd95SBruce Richardson rte_tel_data_start_dict(d); 15099a2dd95SBruce Richardson rte_tel_data_add_dict_string(d, "version", telemetry_version); 15199a2dd95SBruce Richardson rte_tel_data_add_dict_int(d, "pid", getpid()); 15299a2dd95SBruce Richardson rte_tel_data_add_dict_int(d, "max_output_len", MAX_OUTPUT_LEN); 15399a2dd95SBruce Richardson return 0; 15499a2dd95SBruce Richardson } 15599a2dd95SBruce Richardson 15699a2dd95SBruce Richardson static int 15799a2dd95SBruce Richardson command_help(const char *cmd __rte_unused, const char *params, 15899a2dd95SBruce Richardson struct rte_tel_data *d) 15999a2dd95SBruce Richardson { 16099a2dd95SBruce Richardson int i; 1612607c9cbSBruce Richardson /* if no parameters return our own help text */ 1622607c9cbSBruce Richardson const char *to_lookup = (params == NULL ? cmd : params); 16399a2dd95SBruce Richardson 16499a2dd95SBruce Richardson rte_tel_data_start_dict(d); 16599a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 16699a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 1672607c9cbSBruce Richardson if (strcmp(to_lookup, callbacks[i].cmd) == 0) { 1682607c9cbSBruce Richardson if (params == NULL) 1692607c9cbSBruce Richardson rte_tel_data_string(d, callbacks[i].help); 1702607c9cbSBruce Richardson else 1712607c9cbSBruce Richardson rte_tel_data_add_dict_string(d, params, callbacks[i].help); 17299a2dd95SBruce Richardson break; 17399a2dd95SBruce Richardson } 17499a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 17599a2dd95SBruce Richardson if (i == num_callbacks) 17699a2dd95SBruce Richardson return -1; 17799a2dd95SBruce Richardson return 0; 17899a2dd95SBruce Richardson } 17999a2dd95SBruce Richardson 18099a2dd95SBruce Richardson static int 18199a2dd95SBruce Richardson container_to_json(const struct rte_tel_data *d, char *out_buf, size_t buf_len) 18299a2dd95SBruce Richardson { 18399a2dd95SBruce Richardson size_t used = 0; 18499a2dd95SBruce Richardson unsigned int i; 18599a2dd95SBruce Richardson 186cc4f33d9SBruce Richardson if (d->type != TEL_DICT && d->type != TEL_ARRAY_UINT && 1875a36d531SBruce Richardson d->type != TEL_ARRAY_INT && d->type != TEL_ARRAY_STRING) 18899a2dd95SBruce Richardson return snprintf(out_buf, buf_len, "null"); 18999a2dd95SBruce Richardson 190324ec1dfSJonathan Erb if (d->type == TEL_DICT) 191324ec1dfSJonathan Erb used = rte_tel_json_empty_obj(out_buf, buf_len, 0); 192324ec1dfSJonathan Erb else 19399a2dd95SBruce Richardson used = rte_tel_json_empty_array(out_buf, buf_len, 0); 194324ec1dfSJonathan Erb 195cc4f33d9SBruce Richardson if (d->type == TEL_ARRAY_UINT) 19699a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 1972f4520cdSBruce Richardson used = rte_tel_json_add_array_uint(out_buf, 19899a2dd95SBruce Richardson buf_len, used, 199cc4f33d9SBruce Richardson d->data.array[i].uval); 2005a36d531SBruce Richardson if (d->type == TEL_ARRAY_INT) 20199a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 20299a2dd95SBruce Richardson used = rte_tel_json_add_array_int(out_buf, 20399a2dd95SBruce Richardson buf_len, used, 20499a2dd95SBruce Richardson d->data.array[i].ival); 2055a36d531SBruce Richardson if (d->type == TEL_ARRAY_STRING) 20699a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 20799a2dd95SBruce Richardson used = rte_tel_json_add_array_string(out_buf, 20899a2dd95SBruce Richardson buf_len, used, 20999a2dd95SBruce Richardson d->data.array[i].sval); 2105a36d531SBruce Richardson if (d->type == TEL_DICT) 211d2671e64SRadu Nicolau for (i = 0; i < d->data_len; i++) { 212d2671e64SRadu Nicolau const struct tel_dict_entry *v = &d->data.dict[i]; 213d2671e64SRadu Nicolau switch (v->type) { 214d2671e64SRadu Nicolau case RTE_TEL_STRING_VAL: 215d2671e64SRadu Nicolau used = rte_tel_json_add_obj_str(out_buf, 216d2671e64SRadu Nicolau buf_len, used, 217d2671e64SRadu Nicolau v->name, v->value.sval); 218d2671e64SRadu Nicolau break; 219d2671e64SRadu Nicolau case RTE_TEL_INT_VAL: 220d2671e64SRadu Nicolau used = rte_tel_json_add_obj_int(out_buf, 221d2671e64SRadu Nicolau buf_len, used, 222d2671e64SRadu Nicolau v->name, v->value.ival); 223d2671e64SRadu Nicolau break; 2242d2c55e4SBruce Richardson case RTE_TEL_UINT_VAL: 2252f4520cdSBruce Richardson used = rte_tel_json_add_obj_uint(out_buf, 226d2671e64SRadu Nicolau buf_len, used, 227cc4f33d9SBruce Richardson v->name, v->value.uval); 228d2671e64SRadu Nicolau break; 229d2671e64SRadu Nicolau case RTE_TEL_CONTAINER: 230d2671e64SRadu Nicolau { 2316ffce648SBruce Richardson char *temp = malloc(buf_len); 2326ffce648SBruce Richardson if (temp == NULL) 2336ffce648SBruce Richardson break; 2346ffce648SBruce Richardson *temp = '\0'; /* ensure valid string */ 2356ffce648SBruce Richardson 236d2671e64SRadu Nicolau const struct container *cont = 237d2671e64SRadu Nicolau &v->value.container; 238d2671e64SRadu Nicolau if (container_to_json(cont->data, 239d2671e64SRadu Nicolau temp, buf_len) != 0) 240d2671e64SRadu Nicolau used = rte_tel_json_add_obj_json( 241d2671e64SRadu Nicolau out_buf, 242d2671e64SRadu Nicolau buf_len, used, 243d2671e64SRadu Nicolau v->name, temp); 244d2671e64SRadu Nicolau if (!cont->keep) 245d2671e64SRadu Nicolau rte_tel_data_free(cont->data); 2466ffce648SBruce Richardson free(temp); 247d2671e64SRadu Nicolau break; 248d2671e64SRadu Nicolau } 249d2671e64SRadu Nicolau } 250d2671e64SRadu Nicolau } 251d2671e64SRadu Nicolau 25299a2dd95SBruce Richardson return used; 25399a2dd95SBruce Richardson } 25499a2dd95SBruce Richardson 25599a2dd95SBruce Richardson static void 25699a2dd95SBruce Richardson output_json(const char *cmd, const struct rte_tel_data *d, int s) 25799a2dd95SBruce Richardson { 25899a2dd95SBruce Richardson char out_buf[MAX_OUTPUT_LEN]; 25999a2dd95SBruce Richardson 26099a2dd95SBruce Richardson char *cb_data_buf; 26199a2dd95SBruce Richardson size_t buf_len, prefix_used, used = 0; 26299a2dd95SBruce Richardson unsigned int i; 26399a2dd95SBruce Richardson 26499a2dd95SBruce Richardson RTE_BUILD_BUG_ON(sizeof(out_buf) < MAX_CMD_LEN + 26599a2dd95SBruce Richardson RTE_TEL_MAX_SINGLE_STRING_LEN + 10); 2660f3d92f3SBruce Richardson 2670f3d92f3SBruce Richardson prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", 2680f3d92f3SBruce Richardson MAX_CMD_LEN, cmd); 2690f3d92f3SBruce Richardson cb_data_buf = &out_buf[prefix_used]; 2700f3d92f3SBruce Richardson buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */ 2710f3d92f3SBruce Richardson 27299a2dd95SBruce Richardson switch (d->type) { 2735a36d531SBruce Richardson case TEL_NULL: 2740f3d92f3SBruce Richardson used = strlcpy(cb_data_buf, "null", buf_len); 27599a2dd95SBruce Richardson break; 2760f3d92f3SBruce Richardson 2775a36d531SBruce Richardson case TEL_STRING: 278babc5214SBruce Richardson used = rte_tel_json_str(cb_data_buf, buf_len, 0, d->data.str); 27999a2dd95SBruce Richardson break; 28099a2dd95SBruce Richardson 2815a36d531SBruce Richardson case TEL_DICT: 28299a2dd95SBruce Richardson used = rte_tel_json_empty_obj(cb_data_buf, buf_len, 0); 28399a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) { 28499a2dd95SBruce Richardson const struct tel_dict_entry *v = &d->data.dict[i]; 28599a2dd95SBruce Richardson switch (v->type) { 28699a2dd95SBruce Richardson case RTE_TEL_STRING_VAL: 28799a2dd95SBruce Richardson used = rte_tel_json_add_obj_str(cb_data_buf, 28899a2dd95SBruce Richardson buf_len, used, 28999a2dd95SBruce Richardson v->name, v->value.sval); 29099a2dd95SBruce Richardson break; 29199a2dd95SBruce Richardson case RTE_TEL_INT_VAL: 29299a2dd95SBruce Richardson used = rte_tel_json_add_obj_int(cb_data_buf, 29399a2dd95SBruce Richardson buf_len, used, 29499a2dd95SBruce Richardson v->name, v->value.ival); 29599a2dd95SBruce Richardson break; 2962d2c55e4SBruce Richardson case RTE_TEL_UINT_VAL: 2972f4520cdSBruce Richardson used = rte_tel_json_add_obj_uint(cb_data_buf, 29899a2dd95SBruce Richardson buf_len, used, 299cc4f33d9SBruce Richardson v->name, v->value.uval); 30099a2dd95SBruce Richardson break; 30199a2dd95SBruce Richardson case RTE_TEL_CONTAINER: 30299a2dd95SBruce Richardson { 3036ffce648SBruce Richardson char *temp = malloc(buf_len); 3046ffce648SBruce Richardson if (temp == NULL) 3056ffce648SBruce Richardson break; 3066ffce648SBruce Richardson *temp = '\0'; /* ensure valid string */ 3076ffce648SBruce Richardson 30899a2dd95SBruce Richardson const struct container *cont = 30999a2dd95SBruce Richardson &v->value.container; 31099a2dd95SBruce Richardson if (container_to_json(cont->data, 31199a2dd95SBruce Richardson temp, buf_len) != 0) 31299a2dd95SBruce Richardson used = rte_tel_json_add_obj_json( 31399a2dd95SBruce Richardson cb_data_buf, 31499a2dd95SBruce Richardson buf_len, used, 31599a2dd95SBruce Richardson v->name, temp); 31699a2dd95SBruce Richardson if (!cont->keep) 31799a2dd95SBruce Richardson rte_tel_data_free(cont->data); 3186ffce648SBruce Richardson free(temp); 31999a2dd95SBruce Richardson } 32099a2dd95SBruce Richardson } 32199a2dd95SBruce Richardson } 32299a2dd95SBruce Richardson break; 3230f3d92f3SBruce Richardson 3245a36d531SBruce Richardson case TEL_ARRAY_STRING: 3255a36d531SBruce Richardson case TEL_ARRAY_INT: 326cc4f33d9SBruce Richardson case TEL_ARRAY_UINT: 3275a36d531SBruce Richardson case TEL_ARRAY_CONTAINER: 32899a2dd95SBruce Richardson used = rte_tel_json_empty_array(cb_data_buf, buf_len, 0); 32999a2dd95SBruce Richardson for (i = 0; i < d->data_len; i++) 3305a36d531SBruce Richardson if (d->type == TEL_ARRAY_STRING) 33199a2dd95SBruce Richardson used = rte_tel_json_add_array_string( 33299a2dd95SBruce Richardson cb_data_buf, 33399a2dd95SBruce Richardson buf_len, used, 33499a2dd95SBruce Richardson d->data.array[i].sval); 3355a36d531SBruce Richardson else if (d->type == TEL_ARRAY_INT) 33699a2dd95SBruce Richardson used = rte_tel_json_add_array_int(cb_data_buf, 33799a2dd95SBruce Richardson buf_len, used, 33899a2dd95SBruce Richardson d->data.array[i].ival); 339cc4f33d9SBruce Richardson else if (d->type == TEL_ARRAY_UINT) 3402f4520cdSBruce Richardson used = rte_tel_json_add_array_uint(cb_data_buf, 34199a2dd95SBruce Richardson buf_len, used, 342cc4f33d9SBruce Richardson d->data.array[i].uval); 3435a36d531SBruce Richardson else if (d->type == TEL_ARRAY_CONTAINER) { 3446ffce648SBruce Richardson char *temp = malloc(buf_len); 3456ffce648SBruce Richardson if (temp == NULL) 3466ffce648SBruce Richardson break; 3476ffce648SBruce Richardson *temp = '\0'; /* ensure valid string */ 3486ffce648SBruce Richardson 34999a2dd95SBruce Richardson const struct container *rec_data = 35099a2dd95SBruce Richardson &d->data.array[i].container; 35199a2dd95SBruce Richardson if (container_to_json(rec_data->data, 35299a2dd95SBruce Richardson temp, buf_len) != 0) 35399a2dd95SBruce Richardson used = rte_tel_json_add_array_json( 35499a2dd95SBruce Richardson cb_data_buf, 35599a2dd95SBruce Richardson buf_len, used, temp); 35699a2dd95SBruce Richardson if (!rec_data->keep) 35799a2dd95SBruce Richardson rte_tel_data_free(rec_data->data); 3586ffce648SBruce Richardson free(temp); 35999a2dd95SBruce Richardson } 36099a2dd95SBruce Richardson break; 36199a2dd95SBruce Richardson } 3620f3d92f3SBruce Richardson used += prefix_used; 3630f3d92f3SBruce Richardson used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); 36499a2dd95SBruce Richardson if (write(s, out_buf, used) < 0) 36599a2dd95SBruce Richardson perror("Error writing to socket"); 36699a2dd95SBruce Richardson } 36799a2dd95SBruce Richardson 36899a2dd95SBruce Richardson static void 369*ceb5914cSRobin Jarry perform_command(const struct cmd_callback *cb, const char *cmd, const char *param, int s) 37099a2dd95SBruce Richardson { 371ff50c4f9SChengwen Feng struct rte_tel_data data = {0}; 372*ceb5914cSRobin Jarry int ret; 37399a2dd95SBruce Richardson 374*ceb5914cSRobin Jarry if (cb->fn_arg != NULL) 375*ceb5914cSRobin Jarry ret = cb->fn_arg(cmd, param, cb->arg, &data); 376*ceb5914cSRobin Jarry else 377*ceb5914cSRobin Jarry ret = cb->fn(cmd, param, &data); 378*ceb5914cSRobin Jarry 37999a2dd95SBruce Richardson if (ret < 0) { 38099a2dd95SBruce Richardson char out_buf[MAX_CMD_LEN + 10]; 38199a2dd95SBruce Richardson int used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}", 38299a2dd95SBruce Richardson MAX_CMD_LEN, cmd ? cmd : "none"); 38399a2dd95SBruce Richardson if (write(s, out_buf, used) < 0) 38499a2dd95SBruce Richardson perror("Error writing to socket"); 38599a2dd95SBruce Richardson return; 38699a2dd95SBruce Richardson } 38799a2dd95SBruce Richardson output_json(cmd, &data, s); 38899a2dd95SBruce Richardson } 38999a2dd95SBruce Richardson 39099a2dd95SBruce Richardson static int 39199a2dd95SBruce Richardson unknown_command(const char *cmd __rte_unused, const char *params __rte_unused, 39299a2dd95SBruce Richardson struct rte_tel_data *d) 39399a2dd95SBruce Richardson { 3945a36d531SBruce Richardson return d->type = TEL_NULL; 39599a2dd95SBruce Richardson } 39699a2dd95SBruce Richardson 39799a2dd95SBruce Richardson static void * 39899a2dd95SBruce Richardson client_handler(void *sock_id) 39999a2dd95SBruce Richardson { 40099a2dd95SBruce Richardson int s = (int)(uintptr_t)sock_id; 40199a2dd95SBruce Richardson char buffer[1024]; 40299a2dd95SBruce Richardson char info_str[1024]; 40399a2dd95SBruce Richardson snprintf(info_str, sizeof(info_str), 40499a2dd95SBruce Richardson "{\"version\":\"%s\",\"pid\":%d,\"max_output_len\":%d}", 40599a2dd95SBruce Richardson telemetry_version, getpid(), MAX_OUTPUT_LEN); 40699a2dd95SBruce Richardson if (write(s, info_str, strlen(info_str)) < 0) { 40780b7993bSDavid Marchand TMTY_LOG_LINE(DEBUG, "Socket write base info to client failed"); 408e14bb5f1SShaowei Sun goto exit; 40999a2dd95SBruce Richardson } 41099a2dd95SBruce Richardson 41199a2dd95SBruce Richardson /* receive data is not null terminated */ 41299a2dd95SBruce Richardson int bytes = read(s, buffer, sizeof(buffer) - 1); 41399a2dd95SBruce Richardson while (bytes > 0) { 41499a2dd95SBruce Richardson buffer[bytes] = 0; 41599a2dd95SBruce Richardson const char *cmd = strtok(buffer, ","); 41699a2dd95SBruce Richardson const char *param = strtok(NULL, "\0"); 417*ceb5914cSRobin Jarry struct cmd_callback cb = {.fn = unknown_command}; 41899a2dd95SBruce Richardson int i; 41999a2dd95SBruce Richardson 42099a2dd95SBruce Richardson if (cmd && strlen(cmd) < MAX_CMD_LEN) { 42199a2dd95SBruce Richardson rte_spinlock_lock(&callback_sl); 42299a2dd95SBruce Richardson for (i = 0; i < num_callbacks; i++) 42399a2dd95SBruce Richardson if (strcmp(cmd, callbacks[i].cmd) == 0) { 424*ceb5914cSRobin Jarry cb = callbacks[i]; 42599a2dd95SBruce Richardson break; 42699a2dd95SBruce Richardson } 42799a2dd95SBruce Richardson rte_spinlock_unlock(&callback_sl); 42899a2dd95SBruce Richardson } 429*ceb5914cSRobin Jarry perform_command(&cb, cmd, param, s); 43099a2dd95SBruce Richardson 43199a2dd95SBruce Richardson bytes = read(s, buffer, sizeof(buffer) - 1); 43299a2dd95SBruce Richardson } 433e14bb5f1SShaowei Sun exit: 43499a2dd95SBruce Richardson close(s); 4353560f9a4STyler Retzlaff rte_atomic_fetch_sub_explicit(&v2_clients, 1, rte_memory_order_relaxed); 43699a2dd95SBruce Richardson return NULL; 43799a2dd95SBruce Richardson } 43899a2dd95SBruce Richardson 43999a2dd95SBruce Richardson static void * 44099a2dd95SBruce Richardson socket_listener(void *socket) 44199a2dd95SBruce Richardson { 44299a2dd95SBruce Richardson while (1) { 44399a2dd95SBruce Richardson pthread_t th; 444c53a5f3eSChengwen Feng int rc; 44599a2dd95SBruce Richardson struct socket *s = (struct socket *)socket; 44699a2dd95SBruce Richardson int s_accepted = accept(s->sock, NULL, NULL); 44799a2dd95SBruce Richardson if (s_accepted < 0) { 4480e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with accept, telemetry thread quitting"); 44999a2dd95SBruce Richardson return NULL; 45099a2dd95SBruce Richardson } 45199a2dd95SBruce Richardson if (s->num_clients != NULL) { 4523560f9a4STyler Retzlaff uint16_t conns = rte_atomic_load_explicit(s->num_clients, 4533560f9a4STyler Retzlaff rte_memory_order_relaxed); 45499a2dd95SBruce Richardson if (conns >= MAX_CONNECTIONS) { 45599a2dd95SBruce Richardson close(s_accepted); 45699a2dd95SBruce Richardson continue; 45799a2dd95SBruce Richardson } 4583560f9a4STyler Retzlaff rte_atomic_fetch_add_explicit(s->num_clients, 1, 4593560f9a4STyler Retzlaff rte_memory_order_relaxed); 46099a2dd95SBruce Richardson } 461c53a5f3eSChengwen Feng rc = pthread_create(&th, NULL, s->fn, 462c53a5f3eSChengwen Feng (void *)(uintptr_t)s_accepted); 463c53a5f3eSChengwen Feng if (rc != 0) { 4640e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with create client thread: %s", 465c53a5f3eSChengwen Feng strerror(rc)); 466c53a5f3eSChengwen Feng close(s_accepted); 467c53a5f3eSChengwen Feng if (s->num_clients != NULL) 4683560f9a4STyler Retzlaff rte_atomic_fetch_sub_explicit(s->num_clients, 1, 4693560f9a4STyler Retzlaff rte_memory_order_relaxed); 470c53a5f3eSChengwen Feng continue; 471c53a5f3eSChengwen Feng } 47299a2dd95SBruce Richardson pthread_detach(th); 47399a2dd95SBruce Richardson } 47499a2dd95SBruce Richardson return NULL; 47599a2dd95SBruce Richardson } 47699a2dd95SBruce Richardson 47799a2dd95SBruce Richardson static inline char * 47899a2dd95SBruce Richardson get_socket_path(const char *runtime_dir, const int version) 47999a2dd95SBruce Richardson { 48099a2dd95SBruce Richardson static char path[PATH_MAX]; 48199a2dd95SBruce Richardson snprintf(path, sizeof(path), "%s/dpdk_telemetry.v%d", 48299a2dd95SBruce Richardson strlen(runtime_dir) ? runtime_dir : "/tmp", version); 48399a2dd95SBruce Richardson return path; 48499a2dd95SBruce Richardson } 48599a2dd95SBruce Richardson 48699a2dd95SBruce Richardson static void 48799a2dd95SBruce Richardson unlink_sockets(void) 48899a2dd95SBruce Richardson { 48999a2dd95SBruce Richardson if (v2_socket.path[0]) 49099a2dd95SBruce Richardson unlink(v2_socket.path); 49199a2dd95SBruce Richardson if (v1_socket.path[0]) 49299a2dd95SBruce Richardson unlink(v1_socket.path); 49399a2dd95SBruce Richardson } 49499a2dd95SBruce Richardson 49599a2dd95SBruce Richardson static int 49699a2dd95SBruce Richardson create_socket(char *path) 49799a2dd95SBruce Richardson { 49899a2dd95SBruce Richardson int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); 49999a2dd95SBruce Richardson if (sock < 0) { 5000e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with socket creation, %s", strerror(errno)); 50199a2dd95SBruce Richardson return -1; 50299a2dd95SBruce Richardson } 50399a2dd95SBruce Richardson 50499a2dd95SBruce Richardson struct sockaddr_un sun = {.sun_family = AF_UNIX}; 50599a2dd95SBruce Richardson strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 5060e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Attempting socket bind to path '%s'", path); 50785e21b77SBruce Richardson 50899a2dd95SBruce Richardson if (bind(sock, (void *) &sun, sizeof(sun)) < 0) { 509d5252f7dSBruce Richardson struct stat st; 510d5252f7dSBruce Richardson 5110e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Initial bind to socket '%s' failed.", path); 51285e21b77SBruce Richardson 51385e21b77SBruce Richardson /* first check if we have a runtime dir */ 51485e21b77SBruce Richardson if (stat(socket_dir, &st) < 0 || !S_ISDIR(st.st_mode)) { 5150e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Cannot access DPDK runtime directory: %s", socket_dir); 51685e21b77SBruce Richardson close(sock); 51785e21b77SBruce Richardson return -ENOENT; 51885e21b77SBruce Richardson } 51985e21b77SBruce Richardson 52085e21b77SBruce Richardson /* check if current socket is active */ 52185e21b77SBruce Richardson if (connect(sock, (void *)&sun, sizeof(sun)) == 0) { 52285e21b77SBruce Richardson close(sock); 52385e21b77SBruce Richardson return -EADDRINUSE; 52485e21b77SBruce Richardson } 52585e21b77SBruce Richardson 52685e21b77SBruce Richardson /* socket is not active, delete and attempt rebind */ 5270e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Attempting unlink and retrying bind"); 52885e21b77SBruce Richardson unlink(sun.sun_path); 52985e21b77SBruce Richardson if (bind(sock, (void *) &sun, sizeof(sun)) < 0) { 5300e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error binding socket: %s", strerror(errno)); 53185e21b77SBruce Richardson close(sock); 53285e21b77SBruce Richardson return -errno; /* if unlink failed, this will be -EADDRINUSE as above */ 53385e21b77SBruce Richardson } 53499a2dd95SBruce Richardson } 53599a2dd95SBruce Richardson 53699a2dd95SBruce Richardson if (listen(sock, 1) < 0) { 5370e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error calling listen for socket: %s", strerror(errno)); 53885e21b77SBruce Richardson unlink(sun.sun_path); 53985e21b77SBruce Richardson close(sock); 54085e21b77SBruce Richardson return -errno; 54199a2dd95SBruce Richardson } 5420e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Socket creation and binding ok"); 54399a2dd95SBruce Richardson 54499a2dd95SBruce Richardson return sock; 54599a2dd95SBruce Richardson } 54699a2dd95SBruce Richardson 547027c931bSThomas Monjalon static void 548027c931bSThomas Monjalon set_thread_name(pthread_t id __rte_unused, const char *name __rte_unused) 549027c931bSThomas Monjalon { 550027c931bSThomas Monjalon #if defined RTE_EXEC_ENV_LINUX && defined __GLIBC__ && defined __GLIBC_PREREQ 551027c931bSThomas Monjalon #if __GLIBC_PREREQ(2, 12) 552027c931bSThomas Monjalon pthread_setname_np(id, name); 553027c931bSThomas Monjalon #endif 554027c931bSThomas Monjalon #elif defined RTE_EXEC_ENV_FREEBSD 555027c931bSThomas Monjalon pthread_set_name_np(id, name); 556027c931bSThomas Monjalon #endif 557027c931bSThomas Monjalon } 558027c931bSThomas Monjalon 55999a2dd95SBruce Richardson static int 56099a2dd95SBruce Richardson telemetry_legacy_init(void) 56199a2dd95SBruce Richardson { 56299a2dd95SBruce Richardson pthread_t t_old; 563c53a5f3eSChengwen Feng int rc; 56499a2dd95SBruce Richardson 56599a2dd95SBruce Richardson if (num_legacy_callbacks == 1) { 566f5c5ab7eSRobin Jarry TMTY_LOG_LINE(DEBUG, "No legacy callbacks, legacy socket not created"); 56799a2dd95SBruce Richardson return -1; 56899a2dd95SBruce Richardson } 56999a2dd95SBruce Richardson 57099a2dd95SBruce Richardson v1_socket.fn = legacy_client_handler; 57199a2dd95SBruce Richardson if ((size_t) snprintf(v1_socket.path, sizeof(v1_socket.path), 57299a2dd95SBruce Richardson "%s/telemetry", socket_dir) >= sizeof(v1_socket.path)) { 5730e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with socket binding, path too long"); 57499a2dd95SBruce Richardson return -1; 57599a2dd95SBruce Richardson } 57699a2dd95SBruce Richardson v1_socket.sock = create_socket(v1_socket.path); 57785e21b77SBruce Richardson if (v1_socket.sock < 0) { 57885e21b77SBruce Richardson v1_socket.path[0] = '\0'; 57999a2dd95SBruce Richardson return -1; 58085e21b77SBruce Richardson } 581c53a5f3eSChengwen Feng rc = pthread_create(&t_old, NULL, socket_listener, &v1_socket); 582c53a5f3eSChengwen Feng if (rc != 0) { 5830e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with create legacy socket thread: %s", 584c53a5f3eSChengwen Feng strerror(rc)); 585c53a5f3eSChengwen Feng close(v1_socket.sock); 586c53a5f3eSChengwen Feng v1_socket.sock = -1; 587c53a5f3eSChengwen Feng unlink(v1_socket.path); 588c53a5f3eSChengwen Feng v1_socket.path[0] = '\0'; 589c53a5f3eSChengwen Feng return -1; 590c53a5f3eSChengwen Feng } 59199a2dd95SBruce Richardson pthread_setaffinity_np(t_old, sizeof(*thread_cpuset), thread_cpuset); 59262774b78SThomas Monjalon set_thread_name(t_old, "dpdk-telemet-v1"); 5930e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Legacy telemetry socket initialized ok"); 59471ecc415SStephen Hemminger pthread_detach(t_old); 59599a2dd95SBruce Richardson return 0; 59699a2dd95SBruce Richardson } 59799a2dd95SBruce Richardson 59899a2dd95SBruce Richardson static int 59999a2dd95SBruce Richardson telemetry_v2_init(void) 60099a2dd95SBruce Richardson { 60185e21b77SBruce Richardson char spath[sizeof(v2_socket.path)]; 60299a2dd95SBruce Richardson pthread_t t_new; 60385e21b77SBruce Richardson short suffix = 0; 604c53a5f3eSChengwen Feng int rc; 60599a2dd95SBruce Richardson 60699a2dd95SBruce Richardson v2_socket.num_clients = &v2_clients; 60799a2dd95SBruce Richardson rte_telemetry_register_cmd("/", list_commands, 60899a2dd95SBruce Richardson "Returns list of available commands, Takes no parameters"); 60999a2dd95SBruce Richardson rte_telemetry_register_cmd("/info", json_info, 61099a2dd95SBruce Richardson "Returns DPDK Telemetry information. Takes no parameters"); 61199a2dd95SBruce Richardson rte_telemetry_register_cmd("/help", command_help, 61299a2dd95SBruce Richardson "Returns help text for a command. Parameters: string command"); 61399a2dd95SBruce Richardson v2_socket.fn = client_handler; 61485e21b77SBruce Richardson if (strlcpy(spath, get_socket_path(socket_dir, 2), sizeof(spath)) >= sizeof(spath)) { 6150e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with socket binding, path too long"); 61699a2dd95SBruce Richardson return -1; 61799a2dd95SBruce Richardson } 61885e21b77SBruce Richardson memcpy(v2_socket.path, spath, sizeof(v2_socket.path)); 61999a2dd95SBruce Richardson 62099a2dd95SBruce Richardson v2_socket.sock = create_socket(v2_socket.path); 62185e21b77SBruce Richardson while (v2_socket.sock < 0) { 62285e21b77SBruce Richardson /* bail out on unexpected error, or suffix wrap-around */ 62385e21b77SBruce Richardson if (v2_socket.sock != -EADDRINUSE || suffix < 0) { 62485e21b77SBruce Richardson v2_socket.path[0] = '\0'; /* clear socket path */ 62599a2dd95SBruce Richardson return -1; 62685e21b77SBruce Richardson } 62785e21b77SBruce Richardson /* add a suffix to the path if the basic version fails */ 62885e21b77SBruce Richardson if (snprintf(v2_socket.path, sizeof(v2_socket.path), "%s:%d", 62985e21b77SBruce Richardson spath, ++suffix) >= (int)sizeof(v2_socket.path)) { 6300e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with socket binding, path too long"); 63185e21b77SBruce Richardson return -1; 63285e21b77SBruce Richardson } 63385e21b77SBruce Richardson v2_socket.sock = create_socket(v2_socket.path); 63485e21b77SBruce Richardson } 635c53a5f3eSChengwen Feng rc = pthread_create(&t_new, NULL, socket_listener, &v2_socket); 636c53a5f3eSChengwen Feng if (rc != 0) { 6370e21c7c0SDavid Marchand TMTY_LOG_LINE(ERR, "Error with create socket thread: %s", 638c53a5f3eSChengwen Feng strerror(rc)); 639c53a5f3eSChengwen Feng close(v2_socket.sock); 640c53a5f3eSChengwen Feng v2_socket.sock = -1; 641c53a5f3eSChengwen Feng unlink(v2_socket.path); 642c53a5f3eSChengwen Feng v2_socket.path[0] = '\0'; 643c53a5f3eSChengwen Feng return -1; 644c53a5f3eSChengwen Feng } 64599a2dd95SBruce Richardson pthread_setaffinity_np(t_new, sizeof(*thread_cpuset), thread_cpuset); 64662774b78SThomas Monjalon set_thread_name(t_new, "dpdk-telemet-v2"); 64771ecc415SStephen Hemminger pthread_detach(t_new); 64899a2dd95SBruce Richardson atexit(unlink_sockets); 64999a2dd95SBruce Richardson 65099a2dd95SBruce Richardson return 0; 65199a2dd95SBruce Richardson } 65299a2dd95SBruce Richardson 65399a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */ 65499a2dd95SBruce Richardson 65599a2dd95SBruce Richardson int32_t 65668150b90SBruce Richardson rte_telemetry_init(const char *runtime_dir, const char *rte_version, rte_cpuset_t *cpuset) 65799a2dd95SBruce Richardson { 65899a2dd95SBruce Richardson telemetry_version = rte_version; 65999a2dd95SBruce Richardson socket_dir = runtime_dir; 66099a2dd95SBruce Richardson thread_cpuset = cpuset; 66199a2dd95SBruce Richardson 66299a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS 66399a2dd95SBruce Richardson if (telemetry_v2_init() != 0) 66499a2dd95SBruce Richardson return -1; 6650e21c7c0SDavid Marchand TMTY_LOG_LINE(DEBUG, "Telemetry initialized ok"); 66699a2dd95SBruce Richardson telemetry_legacy_init(); 66799a2dd95SBruce Richardson #endif /* RTE_EXEC_ENV_WINDOWS */ 66899a2dd95SBruce Richardson 66999a2dd95SBruce Richardson return 0; 67099a2dd95SBruce Richardson } 671