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