xref: /dpdk/lib/telemetry/telemetry_legacy.c (revision efdf81829076198189ba6ccbb2e5c50270f56992)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2020 Intel Corporation
399a2dd95SBruce Richardson  */
472b452c5SDmitry Kozlyuk #include <ctype.h>
572b452c5SDmitry Kozlyuk #include <errno.h>
699a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
799a2dd95SBruce Richardson #include <unistd.h>
899a2dd95SBruce Richardson #include <sys/socket.h>
999a2dd95SBruce Richardson #include <sys/un.h>
1099a2dd95SBruce Richardson #include <pthread.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 
1999a2dd95SBruce Richardson #include "telemetry_internal.h"
2099a2dd95SBruce Richardson 
2199a2dd95SBruce Richardson #define MAX_LEN 128
2299a2dd95SBruce Richardson #define BUF_SIZE 1024
2399a2dd95SBruce Richardson #define CLIENTS_UNREG_ACTION "\"action\":2"
2499a2dd95SBruce Richardson #define CLIENTS_CMD "\"command\":\"clients\""
2599a2dd95SBruce Richardson #define CLIENTS_DATA "\"data\":{\"client_path\":\""
2699a2dd95SBruce Richardson #define STATS_ACTION "\"action\":0"
2799a2dd95SBruce Richardson #define DATA_REQ_LABEL "\"data\":"
2899a2dd95SBruce Richardson #define TELEMETRY_LEGACY_MAX_CALLBACKS 4
2999a2dd95SBruce Richardson 
3099a2dd95SBruce Richardson 
3199a2dd95SBruce Richardson static int
3299a2dd95SBruce Richardson register_client(const char *cmd __rte_unused,
3399a2dd95SBruce Richardson 		const char *params __rte_unused,
3499a2dd95SBruce Richardson 		char *buffer, int buf_len);
3599a2dd95SBruce Richardson 
3699a2dd95SBruce Richardson struct json_command {
3799a2dd95SBruce Richardson 	char action[MAX_LEN];
3899a2dd95SBruce Richardson 	char cmd[MAX_LEN];
3999a2dd95SBruce Richardson 	char data[MAX_LEN];
4099a2dd95SBruce Richardson 	telemetry_legacy_cb fn;
4199a2dd95SBruce Richardson 
4299a2dd95SBruce Richardson };
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson struct json_command callbacks[TELEMETRY_LEGACY_MAX_CALLBACKS] = {
4599a2dd95SBruce Richardson 		{
4699a2dd95SBruce Richardson 			.action = "\"action\":1",
4799a2dd95SBruce Richardson 			.cmd = CLIENTS_CMD,
4899a2dd95SBruce Richardson 			.data = CLIENTS_DATA,
4999a2dd95SBruce Richardson 			.fn = register_client
5099a2dd95SBruce Richardson 		}
5199a2dd95SBruce Richardson };
5299a2dd95SBruce Richardson int num_legacy_callbacks = 1;
5399a2dd95SBruce Richardson static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER;
5499a2dd95SBruce Richardson 
5599a2dd95SBruce Richardson int
rte_telemetry_legacy_register(const char * cmd,enum rte_telemetry_legacy_data_req data_req,telemetry_legacy_cb fn)5699a2dd95SBruce Richardson rte_telemetry_legacy_register(const char *cmd,
5799a2dd95SBruce Richardson 		enum rte_telemetry_legacy_data_req data_req,
5899a2dd95SBruce Richardson 		telemetry_legacy_cb fn)
5999a2dd95SBruce Richardson {
6099a2dd95SBruce Richardson 	if (fn == NULL)
6199a2dd95SBruce Richardson 		return -EINVAL;
6299a2dd95SBruce Richardson 	if (num_legacy_callbacks >= (int) RTE_DIM(callbacks))
6399a2dd95SBruce Richardson 		return -ENOENT;
6499a2dd95SBruce Richardson 
6599a2dd95SBruce Richardson 	rte_spinlock_lock(&callback_sl);
6699a2dd95SBruce Richardson 	strlcpy(callbacks[num_legacy_callbacks].action, STATS_ACTION, MAX_LEN);
6799a2dd95SBruce Richardson 	snprintf(callbacks[num_legacy_callbacks].cmd, MAX_LEN,
6899a2dd95SBruce Richardson 			"\"command\":\"%s\"", cmd);
6999a2dd95SBruce Richardson 	snprintf(callbacks[num_legacy_callbacks].data, MAX_LEN,
7099a2dd95SBruce Richardson 			data_req ? "%s{\"" : "%snull",
7199a2dd95SBruce Richardson 			DATA_REQ_LABEL);
7299a2dd95SBruce Richardson 	callbacks[num_legacy_callbacks].fn = fn;
7399a2dd95SBruce Richardson 	num_legacy_callbacks++;
7499a2dd95SBruce Richardson 	rte_spinlock_unlock(&callback_sl);
7599a2dd95SBruce Richardson 
7699a2dd95SBruce Richardson 	return 0;
7799a2dd95SBruce Richardson }
7899a2dd95SBruce Richardson 
7999a2dd95SBruce Richardson static int
register_client(const char * cmd __rte_unused,const char * params,char * buffer __rte_unused,int buf_len __rte_unused)8099a2dd95SBruce Richardson register_client(const char *cmd __rte_unused, const char *params,
8199a2dd95SBruce Richardson 		char *buffer __rte_unused, int buf_len __rte_unused)
8299a2dd95SBruce Richardson {
8399a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
8499a2dd95SBruce Richardson 	pthread_t th;
8599a2dd95SBruce Richardson 	char data[BUF_SIZE];
8699a2dd95SBruce Richardson 	int fd;
87c53a5f3eSChengwen Feng 	int rc;
8899a2dd95SBruce Richardson 	struct sockaddr_un addrs;
8999a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
9099a2dd95SBruce Richardson 
9199a2dd95SBruce Richardson 	if (!strchr(params, ':')) {
9299a2dd95SBruce Richardson 		fprintf(stderr, "Invalid data\n");
9399a2dd95SBruce Richardson 		return -1;
9499a2dd95SBruce Richardson 	}
9599a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
9699a2dd95SBruce Richardson 	strlcpy(data, strchr(params, ':'), sizeof(data));
97*efdf8182SZhichao Zeng 	memmove(data, &data[strlen(":\"")], strlen(data));
9899a2dd95SBruce Richardson 	if (!strchr(data, '\"')) {
9999a2dd95SBruce Richardson 		fprintf(stderr, "Invalid client data\n");
10099a2dd95SBruce Richardson 		return -1;
10199a2dd95SBruce Richardson 	}
10299a2dd95SBruce Richardson 	*strchr(data, '\"') = 0;
10399a2dd95SBruce Richardson 
10499a2dd95SBruce Richardson 	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
10599a2dd95SBruce Richardson 	if (fd < 0) {
10699a2dd95SBruce Richardson 		perror("Failed to open socket");
10799a2dd95SBruce Richardson 		return -1;
10899a2dd95SBruce Richardson 	}
10999a2dd95SBruce Richardson 	addrs.sun_family = AF_UNIX;
11099a2dd95SBruce Richardson 	strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path));
11199a2dd95SBruce Richardson 
11299a2dd95SBruce Richardson 	if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) {
11399a2dd95SBruce Richardson 		perror("\nClient connection error\n");
11499a2dd95SBruce Richardson 		close(fd);
11599a2dd95SBruce Richardson 		return -1;
11699a2dd95SBruce Richardson 	}
117c53a5f3eSChengwen Feng 	rc = pthread_create(&th, NULL, &legacy_client_handler,
11899a2dd95SBruce Richardson 				(void *)(uintptr_t)fd);
119c53a5f3eSChengwen Feng 	if (rc != 0) {
120c53a5f3eSChengwen Feng 		fprintf(stderr, "Failed to create legacy client thread: %s\n",
121c53a5f3eSChengwen Feng 			strerror(rc));
122c53a5f3eSChengwen Feng 		close(fd);
123c53a5f3eSChengwen Feng 		return -1;
124c53a5f3eSChengwen Feng 	}
12571ecc415SStephen Hemminger 	pthread_detach(th);
12699a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
12799a2dd95SBruce Richardson 	return 0;
12899a2dd95SBruce Richardson }
12999a2dd95SBruce Richardson 
13099a2dd95SBruce Richardson #ifndef RTE_EXEC_ENV_WINDOWS
13199a2dd95SBruce Richardson 
13299a2dd95SBruce Richardson static int
send_error_response(int s,int err)13399a2dd95SBruce Richardson send_error_response(int s, int err)
13499a2dd95SBruce Richardson {
13599a2dd95SBruce Richardson 	const char *desc;
13699a2dd95SBruce Richardson 	char out_buf[100000];
13799a2dd95SBruce Richardson 
13899a2dd95SBruce Richardson 	switch (err) {
13999a2dd95SBruce Richardson 	case -ENOMEM:
14099a2dd95SBruce Richardson 		desc = "Memory Allocation Error";
14199a2dd95SBruce Richardson 		break;
14299a2dd95SBruce Richardson 	case -EINVAL:
14399a2dd95SBruce Richardson 		desc = "Invalid Argument 404";
14499a2dd95SBruce Richardson 		break;
14599a2dd95SBruce Richardson 	case -EPERM:
14699a2dd95SBruce Richardson 		desc = "Unknown";
14799a2dd95SBruce Richardson 		break;
14899a2dd95SBruce Richardson 	default:
14999a2dd95SBruce Richardson 		/* Default case keeps behaviour of Telemetry library */
15099a2dd95SBruce Richardson 		printf("\nInvalid error type: %d\n", err);
15199a2dd95SBruce Richardson 		return -EINVAL;
15299a2dd95SBruce Richardson 	}
15399a2dd95SBruce Richardson 	int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": "
15499a2dd95SBruce Richardson 			"\"Status Error: %s\", \"data\": null}", desc);
15599a2dd95SBruce Richardson 	if (write(s, out_buf, used) < 0) {
15699a2dd95SBruce Richardson 		perror("Error writing to socket");
15799a2dd95SBruce Richardson 		return -1;
15899a2dd95SBruce Richardson 	}
15999a2dd95SBruce Richardson 	return 0;
16099a2dd95SBruce Richardson }
16199a2dd95SBruce Richardson 
16299a2dd95SBruce Richardson static void
perform_command(telemetry_legacy_cb fn,const char * param,int s)16399a2dd95SBruce Richardson perform_command(telemetry_legacy_cb fn, const char *param, int s)
16499a2dd95SBruce Richardson {
16599a2dd95SBruce Richardson 	char out_buf[100000];
16699a2dd95SBruce Richardson 	int ret, used = 0;
16799a2dd95SBruce Richardson 
16899a2dd95SBruce Richardson 	ret = fn("", param, out_buf, sizeof(out_buf));
16999a2dd95SBruce Richardson 	if (ret < 0) {
17099a2dd95SBruce Richardson 		ret = send_error_response(s, ret);
17199a2dd95SBruce Richardson 		if (ret < 0)
17299a2dd95SBruce Richardson 			printf("\nCould not send error response\n");
17399a2dd95SBruce Richardson 		return;
17499a2dd95SBruce Richardson 	}
17599a2dd95SBruce Richardson 	used += ret;
17699a2dd95SBruce Richardson 	if (write(s, out_buf, used) < 0)
17799a2dd95SBruce Richardson 		perror("Error writing to socket");
17899a2dd95SBruce Richardson }
17999a2dd95SBruce Richardson 
18099a2dd95SBruce Richardson static int
parse_client_request(char * buffer,int buf_len,int s)18199a2dd95SBruce Richardson parse_client_request(char *buffer, int buf_len, int s)
18299a2dd95SBruce Richardson {
18399a2dd95SBruce Richardson 	int i;
18499a2dd95SBruce Richardson 	char *data = buffer + buf_len;
18599a2dd95SBruce Richardson 	telemetry_legacy_cb fn = NULL;
18699a2dd95SBruce Richardson 	const char *valid_sep = ",}";
18799a2dd95SBruce Richardson 	if (buffer[0] != '{' || buffer[buf_len - 1] != '}')
18899a2dd95SBruce Richardson 		return -EPERM;
18999a2dd95SBruce Richardson 
19099a2dd95SBruce Richardson 	if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD)
19199a2dd95SBruce Richardson 			&& strstr(buffer, CLIENTS_DATA))
19299a2dd95SBruce Richardson 		return 0;
19399a2dd95SBruce Richardson 
19499a2dd95SBruce Richardson 	for (i = 0; i < num_legacy_callbacks; i++) {
19599a2dd95SBruce Richardson 		char *action_ptr = strstr(buffer, callbacks[i].action);
19699a2dd95SBruce Richardson 		char *cmd_ptr = strstr(buffer, callbacks[i].cmd);
19799a2dd95SBruce Richardson 		char *data_ptr = strstr(buffer, callbacks[i].data);
19899a2dd95SBruce Richardson 		if (!action_ptr || !cmd_ptr || !data_ptr)
19999a2dd95SBruce Richardson 			continue;
20099a2dd95SBruce Richardson 
20199a2dd95SBruce Richardson 		char action_sep = action_ptr[strlen(callbacks[i].action)];
20299a2dd95SBruce Richardson 		char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)];
20399a2dd95SBruce Richardson 		if (!(strchr(valid_sep, action_sep) && strchr(valid_sep,
20499a2dd95SBruce Richardson 				cmd_sep)))
20599a2dd95SBruce Richardson 			return -EPERM;
20699a2dd95SBruce Richardson 		char data_sep;
20799a2dd95SBruce Richardson 
20899a2dd95SBruce Richardson 		if (!strchr(data_ptr, '{'))
20999a2dd95SBruce Richardson 			data_sep = data_ptr[strlen(callbacks[i].data)];
21099a2dd95SBruce Richardson 		else {
21199a2dd95SBruce Richardson 			if (!strchr(data_ptr, '}'))
21299a2dd95SBruce Richardson 				return -EINVAL;
21399a2dd95SBruce Richardson 			char *data_end = strchr(data_ptr, '}');
21499a2dd95SBruce Richardson 			data = data_ptr + strlen(DATA_REQ_LABEL);
21599a2dd95SBruce Richardson 			data_sep = data_end[1];
21699a2dd95SBruce Richardson 			data_end[1] = 0;
21799a2dd95SBruce Richardson 		}
21899a2dd95SBruce Richardson 		if (!strchr(valid_sep, data_sep))
21999a2dd95SBruce Richardson 			return -EPERM;
22099a2dd95SBruce Richardson 		fn = callbacks[i].fn;
22199a2dd95SBruce Richardson 		break;
22299a2dd95SBruce Richardson 	}
22399a2dd95SBruce Richardson 
22499a2dd95SBruce Richardson 	if (!fn)
22599a2dd95SBruce Richardson 		return -EINVAL;
22699a2dd95SBruce Richardson 	perform_command(fn, data, s);
22799a2dd95SBruce Richardson 	return 0;
22899a2dd95SBruce Richardson }
22999a2dd95SBruce Richardson 
23099a2dd95SBruce Richardson void *
legacy_client_handler(void * sock_id)23199a2dd95SBruce Richardson legacy_client_handler(void *sock_id)
23299a2dd95SBruce Richardson {
23399a2dd95SBruce Richardson 	int s = (int)(uintptr_t)sock_id;
23499a2dd95SBruce Richardson 	int ret;
23599a2dd95SBruce Richardson 	char buffer_recv[BUF_SIZE];
23699a2dd95SBruce Richardson 	/* receive data is not null terminated */
23799a2dd95SBruce Richardson 	int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
23899a2dd95SBruce Richardson 
23999a2dd95SBruce Richardson 	while (bytes > 0) {
24099a2dd95SBruce Richardson 		buffer_recv[bytes] = 0;
24199a2dd95SBruce Richardson 		int i, j;
24299a2dd95SBruce Richardson 		char buffer[BUF_SIZE];
24399a2dd95SBruce Richardson 		for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) {
24499a2dd95SBruce Richardson 			buffer[j] = buffer_recv[i];
24599a2dd95SBruce Richardson 			j += !isspace(buffer_recv[i]);
24699a2dd95SBruce Richardson 		}
24799a2dd95SBruce Richardson 		buffer[j] = 0;
24899a2dd95SBruce Richardson 		ret = parse_client_request(buffer, j, s);
24999a2dd95SBruce Richardson 		if (ret < 0) {
25099a2dd95SBruce Richardson 			ret = send_error_response(s, ret);
25199a2dd95SBruce Richardson 			if (ret < 0)
25299a2dd95SBruce Richardson 				printf("\nCould not send error response\n");
25399a2dd95SBruce Richardson 		}
25499a2dd95SBruce Richardson 		bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1);
25599a2dd95SBruce Richardson 	}
25699a2dd95SBruce Richardson 	close(s);
25799a2dd95SBruce Richardson 	return NULL;
25899a2dd95SBruce Richardson }
25999a2dd95SBruce Richardson 
26099a2dd95SBruce Richardson #endif /* !RTE_EXEC_ENV_WINDOWS */
261