xref: /dpdk/lib/telemetry/telemetry.c (revision ceb5914cd1e153b01bedd9f14a8119355114a21f)
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